Изучаем Python. Программирование игр, визуализация данных, веб-приложения

Мэтиз Эрик

6. Словари

 

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

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

 

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

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

alien.py

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

print(alien_0['color'])

print(alien_0['points'])

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

green

5

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

 

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

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

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

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

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

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

alien_0 = {'color': 'green'}

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

 

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

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

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}

(1) new_points = alien_0['points']

(2)print("You just earned " + str(new_points) + " points!")

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

You just earned 5 points!

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

 

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

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

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

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

print(alien_0)

(1) alien_0['x_position'] = 0

(2)alien_0['y_position'] = 25

print(alien_0)

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

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

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

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

 

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

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

alien_0 = {}

alien_0['color'] = 'green'

alien_0['points'] = 5

print(alien_0)

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

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

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

 

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

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

alien_0 = {'color': 'green'}

print("The alien is " + alien_0['color'] + ".")

alien_0['color'] = 'yellow'

print("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("Original x-position: " + str(alien_0['x_position']))

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

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

(1) if alien_0['speed'] == 'slow':

. .x_increment = 1

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

. .x_increment = 2

else:

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

. .x_increment = 3

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

(2)alien_0['x_position'] = alien_0['x_position'] + x_increment

print("New x-position: " + str(alien_0['x_position']))

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

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

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

Original x-position: 0

New x-position: 2

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

alien_0['speed'] = fast

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

 

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

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

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

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

print(alien_0)

(1) del alien_0['points']

print(alien_0)

Строка (1) приказывает Python удалить ключ 'points' из словаря alien_0, а также удалить значение, связанное с этим ключом. Из вывода видно, что ключ 'points' и его значение 5 исчезли из словаря, но остальные данные остались без изменений:

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

{'color': 'green'}

примечание

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

 

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

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

favorite_languages = {

. .'jen': 'python',

. .'sarah': 'c',

. .'edward': 'ruby',

. .'phil': 'python',

. .}

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

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

примечание

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

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

favorite_languages.py

favorite_languages = {

'jen': 'python',

'sarah': 'c',

'edward': 'ruby',

'phil': 'python',

}

(1) print("Sarah's favorite language is " +

(2) . .favorite_languages['sarah'].title() +

(3) . .".")

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

favorite_languages['sarah']

Этот синтаксис используется в команде вывода (1) , а результат содержит значение, связанное с ключом:

Sarah's favorite language is C.

Этот пример также показывает, как разбить длинную команду print на несколько строк. Слово print короче большинства имен словарей, поэтому есть смысл включить первую часть выводимого текста сразу же за открывающей круглой скобкой (1) . Выберите точку, в которой будет разбиваться вывод, и добавьте оператор конкатенации (+) в конец первой строки (2). Нажмите Enter, а затем клавишу Tab для выравнивания всех последующих строк на один уровень отступа под командой print. Завершив построение вывода, поставьте закрывающую круглую скобку в последней строке блока print (3).

Упражнения

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

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

6-3. Глоссарий: словари Python могут использоваться для моделирования «настоящего» словаря (чтобы не создавать путаницы, назовем его «глоссарием»).

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

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

 

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

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

 

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

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

user_0 = {

. .'username': 'efermi',

. .'first': 'enrico',

. .'last': 'fermi',

. .}

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

user.py

user_0 = {

'username': 'efermi',

'first': 'enrico',

'last': 'fermi',

}

(1) for key, value in user_0.items():

(2) . .print("\nKey: " + key)

(3) . .print("Value: " + value)

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

for k, v in user_0.items()

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

Key: last

Value: fermi

Key: first

Value: enrico

Key: username

Value: efermi

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

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

favorite_languages.py

favorite_languages = {

'jen': 'python',

'sarah': 'c',

'edward': 'ruby',

'phil': 'python',

}

(1) for name, language in favorite_languages.items():

(2) . .print(name.title() + "'s favorite language is " +

. . . .language.title() + ".")

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

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

Jen's favorite language is Python.

Sarah's favorite language is C.

Phil's favorite language is Python.

Edward's favorite language is Ruby.

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

 

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

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

favorite_languages = {

'jen': 'python',

'sarah': 'c',

'edward': 'ruby',

'phil': 'python',

}

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

. .print(name.title())

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

Jen

Sarah

Phil

Edward

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

for name in favorite_languages:

вместо…

for name in favorite_languages.keys():

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

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

favorite_languages = {

'jen': 'python',

'sarah': 'c',

'edward': 'ruby',

'phil': 'python',

}

(1) friends = ['phil', 'sarah']

for name in favorite_languages.keys():

. .print(name.title())

. .

(2) . .if name in friends:

. . . .print(" Hi " + name.title() +

. . . . . .", I see your favorite language is " +

(3) . . . . . .favorite_languages[name].title() + "!")

В точке (1) строится список друзей, для которых должно выводиться сообщение. В цикле выводится имя очередного участника опроса, а затем в точке (2) программа проверяет, входит ли текущее имя в список друзей. Если имя входит в список, выводится специальное приветствие с упоминанием выбранного языка. Чтобы получить язык в точке (3), мы используем имя словаря и текущее значение name как ключ. Имя выводится для всех участников, но только друзья получают еще и специальное сообщение:

Edward

Phil

Hi Phil, I see your favorite language is Python!

Sarah

Hi Sarah, I see your favorite language is C!

Jen

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

favorite_languages = {

. .'jen': 'python',

. .'sarah': 'c',

. .'edward': 'ruby',

. .'phil': 'python',

. .}

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

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

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

Erin, please take our poll!

 

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

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

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

favorite_languages = {

'jen': 'python',

'sarah': 'c',

'edward': 'ruby',

'phil': 'python',

}

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

. .print(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': 'ruby',

'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

Python

Ruby

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

favorite_languages = {

'jen': 'python',

'sarah': 'c',

'edward': 'ruby',

'phil': 'python',

}

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

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

print(language.title())

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

The following languages have been mentioned:

Python

C

Ruby

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

Упражнения

6-4. Глоссарий 2: теперь, когда вы знаете, как перебрать элементы словаря, упростите код из упражнения 6-3, заменив серию команд print циклом, перебирающим ключи и значения словаря. Когда вы будете уверены в том, что цикл работает, добавьте в глоссарий еще пять терминов Python. При повторном запуске программы новые слова и значения должны быть автоматически включены в вывод.

6-5. Реки: создайте словарь с тремя большими реками и странами, по которым протекает каждая река. Одна из возможных пар «ключ—значение» — ‘nile’: ‘egypt’.

• Используйте цикл для вывода сообщения с упоминанием реки и страны — например, «The Nile runs through Egypt.»

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

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

6-6. Опрос: Возьмите за основу код favorite_languages.py (с. 106).

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

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

 

Вложение

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

 

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

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

aliens.py

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

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

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

(1) aliens = [alien_0, alien_1, alien_2]

for alien in aliens:

. .print(alien)

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

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

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

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

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

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

aliens = []

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

(1) for alien_number in range(30):

(2) . .new_alien = {'color': 'green', 'points': 5, 'speed': 'slow'}

(3) . .aliens.append(new_alien)

. .

# Вывод первых 5 пришельцев:

(4)for alien in aliens[:5]:

. .print(alien)

print("...")

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

(5)print("Total number of aliens: " + str(len(aliens)))

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

{'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', 'color': 'green', 'points': 5}

...

Total number of aliens: 30

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

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

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

aliens = []

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

for alien_number in range (0,30):

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

aliens.append(new_alien)

. .

for alien in aliens[0:3]:

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

. . . .alien['color'] = 'yellow'

. . . .alien['speed'] = 'medium'

. . . .alien['points'] = 10

. . . .

# Вывод первых 5 пришельцев:

for alien in aliens[0:5]:

print(alien)

print("...")

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

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

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

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

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

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

...

Цикл можно расширить, добавив блок 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 на с. 108, и сохранить отдельные словари в списке с именем users. Все словари в списке должны иметь одинаковую структуру, чтобы вы могли перебрать список и выполнить с каждым объектом словаря одни и те же операции.

 

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

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

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

pizza.py

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

(1) pizza = {

. .'crust': 'thick',

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

. .}

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

(2)print("You ordered a " + pizza['crust'] + "-crust pizza " +

. ."with the following toppings:")

(3)for topping in pizza['toppings']:

. .print("\t" + topping)

Работа начинается в точке (1) со словаря с информацией о заказанной пицце. С ключом в словаре 'crust' связано строковое значение 'thick'. С другим ключом 'toppings' связано значение-список, в котором хранятся все заказанные дополнения. В точке (2) выводится описание заказа перед созданием пиццы. Чтобы вывести список дополнений, мы используем ключ 'toppings', а Python берет список дополнений из словаря.

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

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

. .mushrooms

. .extra cheese

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

favorite_languages.py

(1) favorite_languages = {

. .'jen': ['python', 'ruby'],

. .'sarah': ['c'],

. .'edward': ['ruby', 'go'],

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

. .}

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

. .print("\n" + name.title() + "'s favorite languages are:")

(3) . .for language in languages:

. . . .print("\t" + language.title())

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

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

Jen's favorite languages are:

. .Python

. .Ruby

Sarah's favorite languages are:

. .C

Phil's favorite languages are:

. .Python

. .Haskell

Edward's favorite languages are:

. .Ruby

. .Go

Чтобы дополнительно усовершенствовать программу, включите в начало цикла 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',

. . . .},

. .}

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

(2) . .print("\nUsername: " + username)

(3) . .full_name = user_info['first'] + " " + user_info['last']

. .location = user_info['location']

(4) . .print("\tFull name: " + full_name.title())

. .print("\tLocation: " + location.title())

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

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

Username: aeinstein

. .Full name: Albert Einstein

. .Location: Princeton

Username: mcurie

. .Full name: Marie Curie

. .Location: Paris

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

Упражнения

6-7. Люди: начните с программы, написанной для упражнения 6-1 (с. 107). Создайте два новых словаря, представляющих разных людей, и сохраните все три словаря в списке с именем people. Переберите элементы списка людей. В процессе перебора выведите всю имеющуюся информацию о каждом человеке.

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

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

6-10. Любимые числа: измените программу из упражнения 6-2 (с. 107), чтобы для каждого человека можно было хранить более одного любимого числа. Выведите имя каждого человека в списке и его любимые числа.

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

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

 

Итоги

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

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