Книга: Изучаем Python: программирование игр, визуализация данных, веб-приложения. 3-е изд. дополненное и переработанное
Назад: 5. Оператор if
Дальше: 7. Ввод данных и циклы while

6. Словари

18186.png

 

В этой главе речь пойдет о словарях — структурах данных, предназначенных для объединения взаимосвязанной информации. Вы узнаете, как получить доступ к информации, хранящейся в словаре, и как изменить ее. Объем данных в словаре практически безграничен, поэтому мы рассмотрим средства перебора данных в словарях. Кроме того, вы научитесь использовать словари, вложенные в списки и другие словари, а также списки, вложенные в словари.

Операции со словарями позволяют более точно моделировать всевозможные реальные объекты. Вы узнаете, как создать словарь, описывающий человека, и сохранить в нем сколько угодно информации об этом человеке. В словаре могут храниться данные об имени, возрасте, месте жительства, профессии и любых других атрибутах. Вы узнаете, как сохранить любые два вида информации, способные образовать пары: список слов и их значений, список имен людей и их любимых чисел, список гор и их высот и т.д.

Простой словарь

Возьмем игру с пришельцами, которые имеют разные цвета и приносят разное количество очков игроку. В следующем простом словаре хранится информация об одном конкретном пришельце:

alien.py

alien_0 = {'color': 'green', 'points': 5}

 

print(alien_0['color'])

print(alien_0['points'])

В словаре alien_0 хранятся два атрибута: цвет (color) и количество очков (points). Последние две строки кода считывают эту информацию из словаря и выводят ее на экран:

green

5

Работа со словарями, как и большинство других новых концепций, требует определенного опыта. Немного поработав со словарями, вы увидите, насколько они эффективны при моделировании реальных ситуаций.

Работа со словарями

Словарь в языке Python представляет собой совокупность пар «ключ — значение». Каждый ключ связывается с неким значением, и программа может получить значение, связанное с заданным ключом. Значением могут быть число, строка, список и даже другой словарь. Собственно, любой объект, создаваемый в программе Python, может стать значением в словаре.

В Python словарь заключается в фигурные скобки {}, в которых приводится последовательность пар «ключ — значение», как в предыдущем примере:

alien_0 = {'color': 'green', 'points': 5}

Пара «ключ — значение» представляет данные, связанные друг с другом. Если вы укажете ключ, то Python вернет связанное с ним значение. Ключ отделяется от значения двоеточием, а пары разделяются запятыми. В словаре может храниться любое количество пар «ключ — значение».

Простейший словарь содержит ровно одну пару «ключ — значение», как в следующей измененной версии словаря alien_0:

alien_0 = {'color': 'green'}

В этом словаре хранится ровно один фрагмент информации о пришельце alien_0, а именно его цвет. Строка 'color' является ключом в словаре; с этим ключом связано значение 'green'.

Обращение к значениям в словаре

Чтобы получить значение, связанное с ключом, укажите имя словаря, а затем ключ в квадратных скобках:

alien.py

alien_0 = {'color': 'green'}

print(alien_0['color'])

Эта конструкция возвращает значение, связанное с ключом 'color', из словаря alien_0:

green

Количество пар «ключ — значение» в словаре не ограничено. Например, вот как выглядит исходный словарь alien_0 с двумя парами «ключ — значение»:

alien_0 = {'color': 'green', 'points': 5}

Теперь программа может получить значение, связанное с любым из ключей в alien_0: color или points. Если игрок сбивает корабль пришельца, то для получения заработанных очков может использоваться код следующего вида:

alien_0 = {'color': 'green', 'points': 5}

 

new_points = alien_0['points']

print(f"You just earned {new_points} points!")

После того как словарь будет определен, интерпретатор извлекает из словаря значение, связанное с ключом 'points'. Затем оно сохраняется в переменной new_points. Последняя строка преобразует целое значение в строку и выводит сообщение с указанием количества заработанных очков:

You just earned 5 points!

Если этот код будет выполняться каждый раз, когда игрок сбивает очередной корабль пришельца, то программа будет получать правильное количество очков.

Добавление новых пар «ключ — значение»

Словари относятся к динамическим структурам данных: в словарь можно в любой момент добавлять новые пары «ключ — значение». Для этого указывается имя словаря, за которым в квадратных скобках следует новый ключ с новым значением.

Добавим в словарь alien_0 еще два атрибута: координаты x и y для вывода изображения пришельца в определенной позиции экрана. Допустим, пришелец должен отображаться у левого края экрана, в 25 пикселах от верхнего края. Система экранных координат обычно располагается в левом верхнем углу, поэтому для размещения пришельца у левого края координата x должна быть равна 0, а координата y — 25:

alien.py

alien_0 = {'color': 'green', 'points': 5}

print(alien_0)

 

alien_0['x_position'] = 0

alien_0['y_position'] = 25

print(alien_0)

Программа начинается с определения того же словаря, с которым мы уже работали ранее. После этого выводится «снимок» текущего состояния словаря. Затем в словарь добавляется новая пара «ключ — значение»: ключ 'x_position' и значение 0. То же самое делается для ключа 'y_position'. При выводе измененного словаря мы видим две дополнительные пары «ключ — значение»:

{'color': 'green', 'points': 5}

{'color': 'green', 'points': 5, 'x_position': 0, 'y_position': 25}

Окончательная версия словаря содержит четыре пары «ключ — значение». Первые две определяют цвет и количество очков, а вторые две — координаты.

Словари сохраняют исходный порядок добавления пар «ключ — значение». Когда вы выводите словарь или перебираете его элементы, то видите элементы в том порядке, в котором они добавлялись в словарь.

Создание пустого словаря

В некоторых ситуациях бывает удобно (или даже необходимо) начать с пустого словаря, а затем добавлять в него новые элементы. Чтобы начать заполнение пустого словаря, определите словарь с пустой парой фигурных скобок, а затем добавляйте новые пары «ключ — значение» (каждая пара в отдельной строке). Например, вот как создается словарь alien_0:

alien.py

alien_0 = {}

 

alien_0['color'] = 'green'

alien_0['points'] = 5

 

print(alien_0)

Программа определяет пустой словарь alien_0, после чего добавляет в него значения для цвета и количества очков. В результате создается словарь, который использовался в предыдущих примерах:

{'color': 'green', 'points': 5}

Обычно пустые словари используются при хранении данных, введенных пользователем, или при написании кода, автоматически генерирующего большое количество пар «ключ — значение».

Изменение значений в словаре

Чтобы изменить значение в словаре, укажите его имя с ключом в квадратных скобках, а затем новое значение, которое должно быть связано с этим ключом. Допустим, в процессе игры цвет пришельца меняется с зеленого на желтый:

alien.py

alien_0 = {'color': 'green'}

print(f"The alien is {alien_0['color']}.")

 

alien_0['color'] = 'yellow'

print(f"The alien is now {alien_0['color']}.")

Сначала определяется словарь alien_0, который содержит данные только о цвете пришельца; затем значение, связанное с ключом 'color', меняется на 'yellow'. Из выходных данных видно, что цвет пришельца действительно сменился с зеленого на желтый:

The alien is green.

The alien is now yellow.

Рассмотрим более интересный пример: отслеживание позиции пришельца, который может двигаться с разными скоростями. Мы сохраним значение, представляющее текущую скорость пришельца, и используем его для определения величины горизонтального смещения:

alien_0 = {'x_position': 0, 'y_position': 25, 'speed': 'medium'}

print(f"Original position: {alien_0['x_position']}")

 

# Пришелец перемещается вправо.

# Вычисляем величину смещения на основании текущей скорости.

❶ if alien_0['speed'] == 'slow':

    x_increment = 1

elif alien_0['speed'] == 'medium':

    x_increment = 2

else:

    # Пришелец двигается быстро.

    x_increment = 3

 

# Новая позиция равна сумме старой позиции и приращения.

❷ alien_0['x_position'] = alien_0['x_position'] + x_increment

 

print(f"New position: {alien_0['x_position']}")

Сначала определяется словарь с исходной позицией (координаты x и y) и скоростью 'medium'. Значения цвета и количества очков для простоты опущены, но с ними этот пример работал бы точно так же. Кроме того, выводится исходное значение x_position.

Цепочка if-elif-else определяет, на какое расстояние пришелец должен переместиться вправо; полученное значение сохраняется в переменной x_increment . Если пришелец двигается медленно ('slow'), то перемещается на одну единицу вправо; при средней скорости ('medium') перемещается на две единицы вправо; наконец, при высокой скорости ('fast') перемещается на три единицы вправо. Вычисленное смещение прибавляется к значению x_position , а результат сохраняется в словаре с ключом x_position.

Позиция пришельца со средней скоростью смещается на две единицы:

Original x-position: 0

New x-position: 2

Получается, что изменение одного значения в словаре изменяет все поведение пришельца. Например, чтобы превратить пришельца, движущегося со средней скоростью, в быстрого, добавьте следующую строку:

alien_0['speed'] = fast

При следующем выполнении кода блок if-elif-else присвоит x_increment большее значение.

Удаление пар «ключ — значение»

Когда информация, хранящаяся в словаре, перестает быть ненужной, пару «ключ — значение» можно полностью удалить с помощью оператора del. При вызове достаточно передать имя словаря и удаляемый ключ.

Так, в следующем примере из словаря alien_0 удаляется ключ 'points' вместе со значением:

alien.py

alien_0 = {'color': 'green', 'points': 5}

print(alien_0)

 

❶ del alien_0['points']

print(alien_0)

Оператор del сообщает Python о необходимости удалить из словаря alien_0 ключ 'points' и связанное с ним значение. Из вывода видно, что ключ 'points' и его значение 5 исчезли из словаря, но остальные данные остались без изменений:

{'color': 'green', 'points': 5}

{'color': 'green'}

ПРИМЕЧАНИЕ

Учтите, что удаление пары «ключ — значение» отменить уже не удастся.

Словарь с однотипными объектами

В предыдущем примере в словаре сохранялась разнообразная информация об одном объекте (пришельце из компьютерной игры). Словарь также может использоваться для хранения одного вида информации о многих объектах. Допустим, вы хотите провести опрос среди коллег и узнать их любимый язык программирования. Результаты простого опроса удобно сохранить в словаре:

favorite_languages.py

favorite_languages = {

    'jen': 'python',

    'sarah': 'c',

    'edward': 'rust',

    'phil': 'python',

    }

Пары в данном словаре разбиты по строкам. Ключами являются имена участников опроса, а значениями — выбранные ими языки. Если вы знаете, что определение словаря потребует нескольких строк, то нажмите клавишу Enter после ввода открывающей фигурной скобки. Добавьте в следующую строку отступ на один уровень (четыре пробела) и запишите первую пару «ключ — значение», поставив за ней запятую. После этого при нажатии Enter ваш редактор кода будет автоматически добавлять во все последующие пары такой же отступ, как у первой.

Завершив определение словаря, добавьте закрывающую фигурную скобку в новой строке после последней пары «ключ — значение» и добавьте в нее отступ на один уровень, чтобы она была выровнена по ключам. За последней парой также рекомендуется поставить запятую, чтобы при необходимости вы смогли легко добавить новую пару «ключ — значение» в следующей строке.

ПРИМЕЧАНИЕ

Во многих редакторах предусмотрены функции, упрощающие форматирование расширенных списков и словарей в описанном стиле. Существуют и другие распространенные способы форматирования длинных словарей — вы можете столкнуться с ними в вашем редакторе или другом источнике.

Этот словарь позволяет легко определить любимый язык конкретного участника опроса:

favorite_languages.py

favorite_languages = {

    'jen': 'python',

    'sarah': 'c',

    'edward': 'rust',

    'phil': 'python',

    }

 

❶ language = favorite_languages['sarah'].title()

print(f"Sarah's favorite language is {language}.")

Чтобы узнать, какой язык выбран пользователем Sarah, мы запрашиваем следу­ющее значение:

favorite_languages['sarah']

Этот синтаксис используется для получения соответствующего языка программирования из словаря и присваивания его переменной language. Создание новой переменной существенно упрощает вызов функции print(). В выходных данных показывается значение, связанное с ключом:

Sarah's favorite language is C.

Вы можете использовать тот же синтаксис для работы с данными любого участника опроса, содержащимися в словаре.

Обращение к значениям методом get()

Использование синтаксиса с ключом в квадратных скобках для получения интересующего вас значения из словаря имеет один потенциальный недостаток: если запрашиваемого ключа не существует, то вы получите сообщение об ошибке.

Посмотрим, что произойдет при запросе количества очков для пришельца, для которого оно не задано:

alien_no_points.py

alien_0 = {'color': 'green', 'speed': 'slow'}

print(alien_0['points'])

На экране появляется трассировка с сообщением об ошибке KeyError:

Traceback (most recent call last):

  File "alien_no_points.py", line 2, in <module>

    print(alien_0['points'])

          ~~~~~~~^^^^^^^^^^

KeyError: 'points'

Более общие способы обработки подобных ошибок рассматриваются в главе 10. Конкретно для словарей можно воспользоваться методом get() и задать значение по умолчанию, которое будет возвращено при отсутствии в словаре требуемого ключа.

В первом аргументе метода get() передается ключ. Во втором необязательном аргументе можно передать значение, которое должно возвращаться при отсутствии ключа:

alien_0 = {'color': 'green', 'speed': 'slow'}

 

point_value = alien_0.get('points', 'No point value assigned.')

print(point_value)

Если ключ 'points' существует в словаре, то вы получите соответствующее значение; если нет — будет получено значение по умолчанию. В данном случае ключа 'points' не существует, поэтому вместо ошибки выводится понятное сообщение:

No point value assigned.

Если есть вероятность, что запрашиваемого ключа не существует, то, возможно, стоит использовать метод get() вместо синтаксиса с квадратными скобками.

ПРИМЕЧАНИЕ

Если второй аргумент при вызове get() опущен, а ключа не существует, то Python вернет специальное значение None — признак того, что значения не существует. Это не ошибка, а специальное значение, указывающее на отсутствие значения. Другие применения None описаны в главе 8.

Упражнения

6.1. Персона. Используйте словарь для сохранения информации об известном вам человеке. Сохраните данные о его имени и фамилии, возрасте и городе, в котором он живет. Словарь должен содержать ключи с такими именами, как first_name, last_name, age и city. Выведите каждый фрагмент информации, хранящийся в словаре.

6.2. Любимые числа. Используйте словарь для хранения любимых чисел людей. Возьмите пять имен и используйте их как ключи словаря. Придумайте любимое число для каждого человека и сохраните его как значение в словаре. Выведите имя каждого человека и его любимое число. Чтобы задача стала более интересной, опросите нескольких друзей и соберите реальные данные для своей программы.

6.3. Глоссарий. Словари Python могут использоваться для моделирования настоящего словаря (чтобы не создавать путаницы, назовем его глоссарием).

 Вспомните пять терминов из области программирования, которые вы узнали в предыдущих главах. Используйте эти слова как ключи глоссария, а их определения — как значения.

 Выведите каждое слово и его определение в отформатированном виде. Напри­мер, вы можете вывести слово, затем двоеточие и определение; или же слово на одной строке, а его определение — с отступом на другой. Используйте символ новой строки (\n) для вставки пустых строк между парами «слово-определение» в выходных данных.

Перебор словаря

Словарь Python может содержать как несколько, так и миллионы пар «ключ — значение». Поскольку в словаре могут храниться большие объемы данных, Python предоставляет средства для перебора элементов словаря. Информация может храниться в словарях по-разному, поэтому предусмотрены различные способы перебора. Программа может перебрать все пары «ключ — значение» в словаре, только ключи или только значения.

Перебор всех пар «ключ — значение»

Прежде чем говорить о разных способах перебора, рассмотрим новый словарь, предназначенный для хранения информации о пользователе сайта. В следующем словаре хранятся данные об имени пользователя и его фамилии:

user.py

user_0 = {

    'username': 'efermi',

    'first': 'enrico',

    'last': 'fermi',

    }

То, что вы уже узнали в этой главе, позволит вам обратиться к любому отдельному атрибуту user_0. Но что, если вы хотите просмотреть все данные из словаря этого пользователя? Для этого можно воспользоваться перебором в цикле for:

user_0 = {

    'username': 'efermi',

    'first': 'enrico',

    'last': 'fermi',

    }

 

for key, value in user_0.items():

    print(f"\nKey: {key}")

    print(f"Value: {value}")

Чтобы написать цикл for для словаря, необходимо создать имена для двух переменных, в которых будут храниться ключ и значение из каждой пары «ключ — значение». Этим двум переменным можно присвоить любые имена — с короткими однобуквенными именами код будет работать точно так же:

for k, v in user_0.items()

Вторая половина оператора for содержит имя словаря, за которым следует вызов метода items(), возвращающий список пар «ключ — значение». Цикл for сохраняет компоненты пары в двух указанных переменных. В предыдущем примере мы используем переменные для вывода каждого ключа key, за которым следует связанное значение value. Элемент "\n" в первом вызове функции print() гарантирует, что перед каждой парой «ключ — значение» в выводе будет вставлена пустая строка:

Key: username

Value: efermi

 

Key: first

Value: enrico

 

Key: last

Value: fermi

Перебор всех пар «ключ — значение» особенно хорошо работает для таких словарей, которые были показаны в примере программы favorite_languages.py, приведенном в подразделе «Словарь с однотипными объектами» ранее в данной главе: то есть для словарей, хранящих один вид информации со многими разными ключами. Перебрав словарь favorite_languages, вы получите имя каждого человека и его любимый язык программирования. Ключ всегда содержит имя, а значение — язык программирования, поэтому в цикле вместо имен key и value используются переменные name и language. Благодаря такому выбору имен читателю кода будет проще следить за тем, что происходит в цикле:

favorite_languages.py

favorite_languages = {

    'jen': 'python',

    'sarah': 'c',

    'edward': 'rust',

    'phil': 'python',

    }

 

for name, language in favorite_languages.items():

    print(f"{name.title()}'s favorite language is {language.title()}.")

Код дает Python указание перебрать все пары «ключ — значение» в словаре. В процессе перебора ключ сохраняется в переменной name, а значение — в переменной language. Благодаря этим описательным именам намного проще понять, что делает вызов функции print().

Всего в нескольких строках кода выводится вся информация из опроса:

Jen's favorite language is Python.

Sarah's favorite language is C.

Edward's favorite language is Rust.

Phil's favorite language is Python.

Этот способ перебора точно так же работает и в том случае, если в словаре будут храниться результаты опроса тысяч и даже миллионов людей.

Перебор всех ключей в словаре

Метод keys() удобен в тех случаях, когда вы не собираетесь работать со всеми значениями в словаре. Переберем словарь favorite_languages и выведем имена всех людей, участвовавших в опросе:

favorite_languages = {

    'jen': 'python',

    'sarah': 'c',

    'edward': 'rust',

    'phil': 'python',

    }

for name in favorite_languages.keys():

    print(name.title())

Этот цикл for дает Python указание извлечь из словаря favorite_languages все ключи и последовательно сохранять их в переменной name. В выходных данных представлены имена всех людей, участвовавших в опросе:

Jen

Sarah

Edward

Phil

На самом деле перебор ключей используется по умолчанию при переборе словаря, поэтому данный код будет работать точно так же, как если бы вы написали

for name in favorite_languages:

вместо

for name in favorite_languages.keys():

Используйте явный вызов метода keys(), если считаете, что он упростит чтение вашего кода, или опустите его при желании.

Чтобы обратиться в цикле к значению, связанному с интересующим вас ключом, используйте текущий ключ. В качестве примера выведем для пары друзей сообщение о выбранном ими языке. Мы переберем имена в словаре, как делали ранее, но когда имя совпадает с именем одного из друзей, программа будет выводить специальное сообщение об их любимом языке:

favorite_languages = {

    --пропуск--

    }

 

friends = ['phil', 'sarah']

for name in favorite_languages.keys():

    print(f"Hi {name.title()}.")

 

❶     if name in friends:

❷         language = favorite_languages[name].title()

        print(f"\t{name.title()}, I see you love {language}!")

Сначала формируется список друзей, для которых должно выводиться сообщение. В цикле выводится имя очередного участника опроса, а затем программа проверяет, входит ли текущее имя в список friends . Если да, то мы извлекаем любимый язык этого человека из словаря на основе текущего значения name в качестве ключа . Затем выводится специальное приветствие с упоминанием выбранного языка.

Выводятся все имена, но для наших друзей выдается специальное сообщение:

Hi Jen.

Hi Sarah.

    Sarah, I see you love C!

Hi Edward.

Hi Phil.

    Phil, I see you love Python!

Метод keys() также может использоваться для проверки того, участвовал ли конкретный человек в опросе:

favorite_languages = {

    --пропуск--

    }

 

if 'erin' not in favorite_languages.keys():

    print("Erin, please take our poll!")

Метод keys() служит не только для перебора: он возвращает список всех ключей, и оператор if просто проверяет, есть ли ключ 'erin' в списке. Его там нет, поэтому программа выводит сообщение:

Erin, please take our poll!

Перебор ключей словаря в определенном порядке

Перебор содержимого словаря возвращает элементы в том порядке, в котором они вставлялись. Тем не менее иногда требуется перебрать элементы словаря в другом порядке.

Один из способов получения элементов в определенном порядке основан на сор­тировке ключей, возвращаемых циклом for. Для получения упорядоченной копии ключей можно воспользоваться функцией sorted():

favorite_languages = {

    'jen': 'python',

    'sarah': 'c',

    'edward': 'rust',

    'phil': 'python',

    }

 

for name in sorted(favorite_languages.keys()):

    print(f"{name.title()}, thank you for taking the poll.")

Этот оператор for не отличается от других операторов for, если не считать того, что метод dictionary.keys() заключен в функцию sorted(). Благодаря данной конструкции Python получает указание выдать список всех ключей в словаре и отсортировать его до перебора элементов. В выводе перечислены все пользователи, участвовавшие в опросе, а их имена упорядочены по алфавиту:

Edward, thank you for taking the poll.

Jen, thank you for taking the poll.

Phil, thank you for taking the poll.

Sarah, thank you for taking the poll.

Перебор всех значений в словаре

Если вас прежде всего интересуют значения, содержащиеся в словаре, то используйте метод values() для получения списка значений без ключей. Например, вы хотите просто получить список всех языков, выбранных в опросе, и вас не интересуют имена людей, выбравших каждый язык:

favorite_languages = {

    'jen': 'python',

    'sarah': 'c',

    'edward': 'rust',

    'phil': 'python',

    }

 

print("The following languages have been mentioned:")

for language in favorite_languages.values():

    print(language.title())

Оператор for считывает каждое значение из словаря и сохраняет его в переменной language. При выводе этих значений будет получен список всех выбранных языков:

The following languages have been mentioned:

Python

C

Rust

Python

Значения извлекаются из словаря, не подвергаясь проверке на возможные повторения. В случае небольших словарей это может быть приемлемо, но в опросах с большим количеством респондентов список будет содержать слишком много дубликатов. Чтобы получить список выбранных языков без повторений, можно воспользоваться множеством (set). Оно в целом похоже на список, но все его элементы должны быть уникальными:

favorite_languages = {

    --пропуск--

    }

 

print("The following languages have been mentioned:")

for language in set(favorite_languages.values()):

    print(language.title())

Когда список, содержащий дубликаты, заключается в функцию set(), Python находит уникальные элементы списка и создает множество из этих элементов. В нашем коде данная функция служит для извлечения уникальных языков из favorite_languages.values().

В результате создается не содержащий дубликатов список языков программирования, упомянутых участниками опроса:

The following languages have been mentioned:

Python

C

Rust

В ходе дальнейшего изучения Python вы часто будете обнаруживать встроенные возможности языка, которые помогают сделать с данными именно то, что вам требуется.

ПРИМЕЧАНИЕ

Множество можно создать прямо в фигурных скобках, разделяя элементы запятыми:

>>> languages = {'python', 'rust', 'python', 'c'}

>>> languages

{'rust', 'python', 'c'}

Словари легко перепутать с множествами, поскольку обе структуры заключаются в фигурные скобки. Когда вы видите фигурные скобки без пар «ключ — значение», скорее всего, перед вами множество. В отличие от списков и словарей, элементы множеств не хранятся в каком-либо определенном порядке.

Упражнения

6.4. Глоссарий 2. Теперь, когда вы знаете, как перебрать элементы словаря, упростите код из упражнения 6.3, заменив серию вызовов функции print() циклом, перебирающим ключи и значения словаря. Когда вы будете уверены в том, что цикл работает, добавьте в глоссарий еще пять терминов Python. При повторном запуске программы новые слова и значения должны быть автоматически добавлены в вывод.

6.5. Реки. Создайте словарь с данными о трех больших реках и странах, по которым протекает каждая из этих рек. Одна из возможных пар «ключ — значение» — 'nile': 'egypt'.

 Используйте цикл для вывода сообщения с упоминанием реки и страны — например, «Нил протекает через Египет».

 Используйте цикл для вывода названия каждой реки, добавленной в словарь.

 Используйте цикл для вывода названия каждой страны, добавленной в словарь.

6.6. Опрос. Возьмите за основу код программы favorite_languages.py из подраздела «Словарь с однотипными объектами» данной главы.

 Создайте список людей, которые должны участвовать в опросе по поводу любимого языка программирования. Добавьте имена, которые уже присутствуют в списке, и имена, которых в нем еще нет.

 Переберите список людей, которые должны участвовать в опросе. Если они уже прошли его, то выведите сообщение с благодарностью за участие. Если еще не проходили, то выведите сообщение с предложением принять участие.

Вложение данных

Иногда бывает нужно сохранить множество словарей в списке или сохранить список как значение элемента словаря. Создание сложных структур такого рода называется вложением (nesting). Вы можете вложить множество словарей в список, список элементов в словарь или даже словарь в другой словарь. Как наглядно показывают следующие примеры, вложение — чрезвычайно мощный механизм.

Список словарей

Словарь alien_0 содержит разнообразную информацию об одном пришельце, но в нем нет места для хранения данных о втором пришельце, не говоря уже об армаде пришельцев. Как смоделировать флот вторжения? Например, можно создать список, в котором каждый элемент представляет собой словарь с информацией о пришельце. Например, следующий код создает список с данными о трех пришельцах:

aliens.py

alien_0 = {'color': 'green', 'points': 5}

alien_1 = {'color': 'yellow', 'points': 10}

alien_2 = {'color': 'red', 'points': 15}

 

❶ aliens = [alien_0, alien_1, alien_2]

 

for alien in aliens:

    print(alien)

Сначала создаются три словаря, каждый из которых представляет отдельного пришельца. Каждый словарь заносится в список aliens . Наконец, программа перебирает список и выводит данные о каждом пришельце:

{'color': 'green', 'points': 5}

{'color': 'yellow', 'points': 10}

{'color': 'red', 'points': 15}

Конечно, в реалистичном примере будут использоваться данные о более чем трех пришельцах, которые будут генерироваться автоматически. В следующем примере функция range() создает флот из 30 пришельцев:

# Создание пустого списка для хранения данных о пришельцах.

aliens = []

 

# Создание 30 зеленых пришельцев.

❶ for alien_number in range(30):

❷     new_alien = {'color': 'green', 'points': 5, 'speed': 'slow'}

❸     aliens.append(new_alien)

 

# Вывод данных о первых пяти пришельцах:

❹ for alien in aliens[:5]:

    print(alien)

print("...")

 

# Вывод количества созданных пришельцев.

print(f"Total number of aliens: {len(aliens)}")

В начале примера список для хранения данных обо всех пришельцах, которые будут созданы, пуст. Функция range() возвращает множество чисел, которое просто сообщает Python, сколько раз должен повторяться цикл. При каждом выполнении цикла создается новый пришелец , который затем добавляется в список aliens . Срез используется для вывода данных о первых пяти пришельцах , а затем выво­дится длина списка (для демонстрации того, что программа действительно сгенери­ровала весь флот из 30 пришельцев):

{'color': 'green', 'points': 5, 'speed': 'slow'}

{'color': 'green', 'points': 5, 'speed': 'slow'}

{'color': 'green', 'points': 5, 'speed': 'slow'}

{'color': 'green', 'points': 5, 'speed': 'slow'}

{'color': 'green', 'points': 5, 'speed': 'slow'}

...

 

Total number of aliens: 30

Все пришельцы обладают одинаковыми характеристиками, но Python рассматривает любого пришельца как отдельный объект, что позволяет изменять атрибуты каждого владельца по отдельности.

Как работать с таким множеством? Представьте, что в этой игре некоторые пришельцы меняют цвет и начинают двигаться быстрее. Когда приходит время смены цветов, мы можем воспользоваться циклом for и оператором if для изменения цвета. Например, чтобы превратить первых трех пришельцев в желтых, двигающихся со средней скоростью и приносящих игроку по 10 очков, можно действовать так:

# Создание пустого списка для хранения данных о пришельцах.

aliens = []

 

# Создание 30 зеленых пришельцев.

for alien_number in range (30):

    new_alien = {'color': 'green', 'points': 5, 'speed': 'slow'}

    aliens.append(new_alien)

 

for alien in aliens[:3]:

    if alien['color'] == 'green':

        alien['color'] = 'yellow'

        alien['speed'] = 'medium'

        alien['points'] = 10

 

# Вывод данных о первых пяти пришельцах:

for alien in aliens[:5]:

    print(alien)

print("...")

Чтобы изменить первых трех пришельцев, мы перебираем элементы среза, содержащего информацию только о них. В данный момент все пришельцы зеленые ('green'), но так будет не всегда, поэтому мы пишем оператор if, который гарантирует, что изменяться будут только зеленые пришельцы. Если пришелец зеленый, то его цвет меняется на желтый ('yellow'), скорость на среднюю ('medium'), а награда увеличивается до 10 очков:

{'color': 'yellow', 'points': 10, 'speed': 'medium'}

{'color': 'yellow', 'points': 10, 'speed': 'medium'}

{'color': 'yellow', 'points': 10, 'speed': 'medium'}

{'color': 'green', 'points': 5, 'speed': 'slow'}

{'color': 'green', 'points': 5, 'speed': 'slow'}

...

Цикл можно расширить, добавив блок elif для превращения желтых пришельцев в красных — быстрых и приносящих игроку по 15 очков. Мы не станем приводить весь код, а цикл выглядит так:

for alien in aliens[0:3]:

    if alien['color'] == 'green':

        alien['color'] = 'yellow'

        alien['speed'] = 'medium'

        alien['points'] = 10

    elif alien['color'] == 'yellow':

        alien['color'] = 'red'

        alien['speed'] = 'fast'

        alien['points'] = 15

Решение с хранением словарей в списке встречается достаточно часто, когда каждый словарь содержит разные атрибуты одного объекта. Например, вы можете создать словарь для каждого пользователя сайта, как это было сделано в программе user.py в подразделе «Перебор всех пар “ключ — значение”» выше в данной главе, и сохранить отдельные словари в списке users. Все словари в списке должны иметь одинаковую структуру, чтобы вы могли перебрать список и выполнить с каждым объектом словаря одни и те же операции.

Список в словаре

Вместо того чтобы помещать словарь в список, иногда бывает удобно совершить обратное действие. Представьте, как бы вы описали в программе заказанную пиццу. Если ограничиться только списком, то сохранить удастся разве что список начинок для пиццы. При использовании словаря список начинок может быть всего лишь одним аспектом описания пиццы.

В следующем примере для каждой пиццы сохраняются два вида информации: тип коржа и список начинок. Последний представляет собой значение, связанное с ключом 'toppings'. Чтобы использовать элементы в списке, нужно указать имя словаря и ключ 'toppings', как и для любого другого значения в словаре. Вместо одного значения будет получен список начинок:

pizza.py

# Сохранение информации о заказанной пицце.

pizza = {

    'crust': 'thick',

    'toppings': ['mushrooms', 'extra cheese'],

    }

 

# Описание заказа.

❶ print(f"You ordered a {pizza['crust']}-crust pizza "

    "with the following toppings:")

 

❷ for topping in pizza['toppings']:

    print(f"\t{topping}")

Работа начинается со словаря, в котором хранится информация о заказанной пицце. С ключом в словаре 'crust' связано строковое значение 'thick'. С другим ключом 'toppings' связано значение-список, в котором хранятся данные обо всех заказанных начинках. Затем выводится описание заказа перед изготовлением пиццы . Если вам нужно разбить длинную строку в вызове функции print(), то выберите точку для разбиения выводимой строки и закончите ее кавычкой. Добавьте в следующую строку отступ, открывающую кавычку и продолжите строку. Python автоматически объединяет все строки, обнаруженные в круглых скобках. Для вывода начинок пишется цикл for . Чтобы вывести список начинок, мы используем ключ 'toppings', а Python берет список начинок из словаря.

Следующее сообщение описывает пиццу, которую мы собираемся создать:

You ordered a thick-crust pizza with the following toppings:

    mushrooms

    extra cheese

Вложение списка в словарь может применяться каждый раз, когда с одним ключом словаря должно быть связано несколько значений. Если бы в предыдущем примере с языками программирования ответы сохранялись в списке, то один участник опроса мог бы выбрать сразу несколько любимых языков.

При переборе словаря значение, связанное с каждым человеком, представляло бы собой список языков (вместо одного языка). В цикле for словаря создается другой цикл для перебора списка языков, связанных с каждым участником:

favorite_languages.py

favorite_languages = {

    'jen': ['python', 'rust'],

    'sarah': ['c'],

    'edward': ['rust', 'go'],

    'phil': ['python', 'haskell'],

    }

 

❶ for name, languages in favorite_languages.items():

    print(f"\n{name.title()}'s favorite languages are:")

❷     for language in languages:

        print(f"\t{language.title()}")

Значение, связанное с каждым именем, теперь представляет собой список. У одних участников единственный любимый язык программирования, у других таких языков несколько. При переборе словаря переменная languages используется для хранения каждого значения из него, поскольку мы знаем, что оно будет представлять собой список. В основном цикле по элементам словаря другой цикл перебирает элементы списка любимых языков каждого участника. Теперь каждый участник опроса может указать сколько угодно любимых языков программирования:

Jen's favorite languages are:

    Python

    Rust

 

Sarah's favorite languages are:

    C

 

Edward's favorite languages are:

    Ruby

    Go

 

Phil's favorite languages are:

    Python

    Haskell

Чтобы дополнительно усовершенствовать программу, добавьте в начало цикла for словаря оператор if для проверки того, выбрал ли данный участник несколько языков программирования (проверка основана на значении len(languages)). Если у участника только один любимый язык, то текст сообщения изменяется, чтобы в нем использовалось единственное число (например, Sarah’s favorite language is C).

ПРИМЕЧАНИЕ

Глубина вложения списков и словарей не должна быть слишком большой. Если вам приходится вкладывать элементы на глубину, существенно бо́льшую, чем в предыдущих примерах, или если вы работаете с чужим кодом, в котором глубина вложения довольно велика, то, скорее всего, у задачи существует более простое решение.

Вложение словарей

Словарь можно вложить в другой словарь, но в таких случаях код быстро усложняется. Например, если на сайте есть несколько пользователей с уникальными именами, то вы можете использовать их имена как ключи в словаре. Информация о каждом пользователе при этом хранится в словаре, который применяется как значение, связанное с именем. В следующем примере о каждом пользователе хранятся три вида информации: имя, фамилия и место жительства. Чтобы получить доступ к этим данным, переберите имена пользователей и словарь с информацией, связанной с каждым именем:

many_users.py

users = {

    'aeinstein': {

        'first': 'albert',

        'last': 'einstein',

        'location': 'princeton',

        },

 

    'mcurie': {

        'first': 'marie',

        'last': 'curie',

        'location': 'paris',

        },

    }

 

❶ for username, user_info in users.items():

❷     print(f"\nUsername: {username}")

❸     full_name = f"{user_info['first']} {user_info['last']}"

    location = user_info['location']

 

❹     print(f"\tFull name: {full_name.title()}")

    print(f"\tLocation: {location.title()}")

В программе определяется словарь users, содержащий два ключа: для пользователей 'aeinstein' и 'mcurie'. Значение, связанное с каждым ключом, представляет собой словарь с именем, фамилией и местом жительства пользователя. В процессе перебора словаря users Python сохраняет каждый ключ в переменной username, а словарь, связанный с каждым именем пользователя, — в переменной user_info. Внутри основного цикла в словаре выводится имя пользователя .

Затем начинается работа с внутренним словарем . Переменная user_info, содержащая словарь с информацией о пользователе, содержит три ключа: 'first', 'last' и 'location'. Каждый ключ используется для создания отформатированных данных, содержащих полное имя и место жительства пользователя, а затем для вывода сводки известной информации о пользователе :

Username: aeinstein

    Full name: Albert Einstein

    Location: Princeton

 

Username: mcurie

    Full name: Marie Curie

    Location: Paris

Обратите внимание на идентичность структур словарей всех пользователей. Хотя Python этого и не требует, наличие единой структуры упрощает работу с вложенными словарями. Если словари разных пользователей будут содержать разные ключи, то код в цикле for заметно усложнится.

Упражнения

6.7. Люди. Начните с программы, написанной для упражнения 6.1. Создайте два новых словаря, представляющих разных людей, и сохраните все три словаря в списке people. Переберите элементы списка людей. В процессе перебора выведите всю имеющуюся информацию о каждом человеке.

6.8. Домашние животные. Создайте несколько словарей, имена которых представляют клички домашних животных. В каждом словаре сохраните информацию о виде животного и имени владельца. Сохраните словари в списке pets. Переберите элементы списка. В процессе перебора выведите всю имеющуюся информацию о каждом животном.

6.9. Любимые места. Создайте словарь favorite_places. Придумайте названия трех мест, которые станут ключами словаря, и сохраните для каждого человека из упражнения 6.7 от одного до трех любимых мест. Чтобы задача стала более интересной, опросите нескольких друзей и соберите реальные данные для своей программы. Переберите данные в словаре, выведите имя каждого человека и его любимые места.

6.10. Любимые числа. Измените программу из упражнения 6.2, чтобы для каждого человека можно было хранить несколько любимых чисел. Выведите имя каждого человека в списке и его любимые числа.

6.11. Города. Создайте словарь cities. Используйте названия трех городов в качестве ключей словаря. Создайте словарь с информацией о каждом городе; добавьте в него страну, в которой расположен город, примерную численность населения и один примечательный факт, относящийся к этому городу. Ключи словаря каждого города должны называться country, population и fact. Выведите название каждого города и всю сохраненную информацию о нем.

6.12. Расширение. Примеры, с которыми мы работаем, стали достаточно сложными, и в них можно вносить разного рода усовершенствования. Воспользуйтесь одним из примеров этой главы и расширьте его: добавьте новые ключи и значения, измените контекст программы или улучшите форматирование вывода.

Резюме

В этой главе вы научились определять словари и работать с хранящейся в них информацией. Вы узнали, как обращаться к отдельным элементам словаря и изменять их, как перебрать всю информацию в словаре. Вы изучили способы перебора пар «ключ — значение», ключей и значений словаря. Кроме того, вы узнали о том, как вкладывать словари в список, списки — в словари, а словари — в другие словари.

В следующей главе вы познакомитесь с циклами while и получением входных данных от пользователей программ. Эта глава будет особенно интересной, поскольку вы наконец-то сможете сделать свои программы интерактивными: они начнут реагировать на действия пользователя.

Назад: 5. Оператор if
Дальше: 7. Ввод данных и циклы while