Словари в Python
В языке программирования Python словари (тип dict
) представляют собой еще одну разновидность структур данных наряду со списками и кортежами. Словарь ‒ это изменяемый (как список) неупорядоченный (в отличие от строк, списков и кортежей) набор элементов "ключ:значение". Такие элементы-пары будем называть записями.
"Неупорядоченный" – значит, что последовательность расположения пар не важна, вследствие чего обращение к элементам по индексам невозможно.
В других языках структуры, схожие со словарями, называются по-другому. Например, в Java подобный тип данных называется отображением.
Чтобы представление о словаре стало более понятным, проведем аналогию с обычным словарем, например, англо-русским. На каждое английское слово в таком словаре есть русское слово-перевод: cat – кошка, dog – собака, table – стол и т. д. Если англо-русский словарь описать с помощью Python, то английские слова можно сделать ключами, а русские – их значениями:
a = {'cat': 'кошка', 'dog': 'собака', 'bird': 'птица'}
Обратите внимание, что для определения словаря используются фигурные скобки. Синтаксис словаря на Питоне описывается такой схемой:
В словаре не может быть двух элементов с одинаковыми ключами. Однако могут быть одинаковые значения у разных ключей. Например, такой вариант словаря корректен:
>>> a = {'cat': 'кошка', 'dog': 'собака', 'bird': 'птица', 'fowl': 'птица'} >>> a {'cat': 'кошка', 'dog': 'собака', 'bird': 'птица', 'fowl': 'птица'}
Мы получили то, что определили. В случае повторения ключей ошибки тоже не будет:
>>> b = {'cat': 'кошка', 'dog': 'собака', 'bird': 'птица', 'dog': 'пёс'} >>> b {'cat': 'кошка', 'dog': 'пёс', 'bird': 'птица'}
Однако добавление записи с ключом, который уже был в словаре, приводит к обновлению его значения.
В нашем примере, если мы хотим решить проблему перевода одного английского слова несколькими русскими, то вместо строкового значения можем использовать список или любой другой структурный тип данных:
>>> b = {'cat': 'кошка', 'dog': ['собака', 'пёс'], 'bird': 'птица'} >>> b {'cat': 'кошка', 'dog': ['собака', 'пёс'], 'bird': 'птица'}
С другой стороны, в качестве ключей нельзя использовать изменяемые типы данных. Это связано с особенностями хранения словаря в памяти компьютера.
>>> a = {'cat': 'кошка', 'dog': 'собака', ['bird', 'fowl']: 'птица'} Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'list'
Однако возможно такое:
>>> a = {'cat': 'кошка', 'dog': 'собака', ('bird', 'fowl'): 'птица'} >>> a {'cat': 'кошка', 'dog': 'собака', ('bird', 'fowl'): 'птица'}
Потому что кортеж ‒ это неизменяемый тип данных (и в данном случае не должен содержать элементов изменяемых типов внутри себя). Другое дело, что в большинстве случаев для удобства пользования словарем ключи не следует делать составными.
В словаре доступ к значениям осуществляется не по индексам, а по ключам, которые заключаются в квадратные скобки (по аналогии с индексами списков):
>>> a['cat'] 'кошка' >>> a['bird'] 'птица'
Но если таким способом попытаться получить значение ключа, которого в словаре нет, то возникнет ошибка:
>>> a {'cat': 'кошка', 'dog': 'собака', 'bird': 'птица'} >>> a['pig'] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'pig'
Исключение можно обработать с помощью инструкции try-except:
animals = {'cat': 'кошка', 'dog': 'собака', 'bird': 'птица'} a_key = input('Введите слово на английском: ') try: print('Его перевод:', animals[a_key]) except KeyError: print('В словаре нет такого слова')
С другой стороны, у словаря как типа данных есть метод get()
, с помощью которого также можно получать значения ключей:
>>> a.get('cat') 'кошка' >>> a.get('bird') 'птица'
Однако когда ключа в словаре нет, метод get()
ведет себя не так, как это было в случае получения значения с помощью квадратных скобок. Вместо выброса исключения, метод возвращает объект None
. Тогда в программе выше вместо конструкции try-except достаточно написать всего одну строчку кода:
print('Его перевод:', animals.get(a_key))
Пример выполнения при вводе отсутствующего ключа:
Введите слово на английском: pig Его перевод: None
В метод get()
можно передать второй аргумент, который при отсутствии ключа в словаре будет подставляться вместо None
.
print('Его перевод:', animals.get(a_key, '-'))
Словари, как и списки, являются изменяемым типом данных: позволительно изменять, добавлять и удалять записи (пары "ключ:значение"). Изначально словарь можно создать пустым (например, d = {}
) и потом заполнить его элементами.
>>> fruits = {} >>> fruits['apple'] = 4 >>> fruits['kiwi'] = 3 >>> fruits {'apple': 4, 'kiwi': 3}
Добавление и изменение имеет одинаковый синтаксис: словарь[ключ] = значение
. Ключ может быть как уже существующим (тогда происходит изменение значения), так и новым (происходит добавление элемента словаря).
>>> fruits['orange'] = 2 >>> fruits['apple'] = 5 >>> fruits {'apple': 5, 'kiwi': 3, 'orange': 2}
Представим, что мы хотим написать программу, которая запрашивает у пользователя ключ и значение. Если ключа нет в словаре, то происходит добавление новой записи в словарь. Если же полученный ключ там уже есть, надо добавить введенное значение к имеющемуся у данного ключа. То есть, перед тем, как выполнять ту или иную операцию, необходимо проверить, есть ли ключ в словаре. В Python это делается с помощью оператора in
:
>>> 'apple' in fruits True >>> 'banana' in fruits False
Тогда программа может выглядеть так:
fruits = {'apple': 5, 'kiwi': 3, 'orange': 2} k = input('Fruit name: ') v = int(input('Number: ')) if k in fruits: fruits[k] += v else: fruits[k] = v print(fruits)
Также как списки словари можно перебирать в цикле for
:
>>> fruits = {'apple': 5, 'kiwi': 3, 'orange': 2} >>> for k in fruits: ... print(k) ... apple kiwi orange
Здесь выражение k in fruits
в заголовке цикла обозначает не то, что в заголовке if
. Там мы проверяли один элемент на вхождение в структуру, а в цикле происходит перебор всех элементов с присваиванием каждого переменной, указанной перед in
. В то же время и там, и здесь обращение к переменной, связанной со словарем, как бы извлекает из него только ключи (дает нам перечень ключей), но не значения.
Конечно, по ключам всегда можно получить значения:
>>> fruits = {'apple': 5, 'kiwi': 3, 'orange': 2} >>> for k in fruits: ... print(fruits[k]) ... 5 3 2
Однако в Python есть другие способы получения перечней ключей, значений, а также их пар ‒ методы keys()
, values()
и items()
. Со значениями все понятно. Они могут понадобиться отдельно от ключей:
>>> nums = {1: 'one', 2: 'two', 3: 'three', 4: 'four'} >>> for v in nums.values(): ... print(v) ... one two three four >>> >>> fruits = {'apple': 5, 'kiwi': 3, 'orange': 2} >>> sum(fruits.values()) 10
Возникает вопрос: зачем нужны наборы ключей или пар, если и так ключи можно получить из имени словаря, а сам словарь и так состоит из пар? На самом деле ключи просто так получить нельзя. То, как используется переменная-словарь в контексте оператора in
, не имеет отношения к обычному обращению к переменной, когда возвращается ее значение. В данном случае это будет целый словарь:
>>> nums {1: 'one', 2: 'two', 3: 'three', 4: 'four'} >>> fruits {'apple': 5, 'kiwi': 3, 'orange': 2}
Теперь посмотрим, что нам вернет метод keys()
:
>>> nums.keys() dict_keys([1, 2, 3, 4]) >>> fruits.keys() dict_keys(['apple', 'kiwi', 'orange'])
Это перечень только ключей словаря, упакованный в особый тип объекта. В случае items()
получаем подобный объект, но состоящий из кортежей (ключ, значение)
.
>>> nums.items() dict_items([(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')])
Объекты типов dict_items
, dict_values
, dict_keys
могут перебираться с помощью цикла for
.
>>> for i in nums.items(): ... print(i) ... (1, 'one') (2, 'two') (3, 'three') (4, 'four')
Обратите внимание, что на каждой итерации цикла из dict_items
извлекается кортеж. Его можно распаковывать сразу в заголовке цикла for
, отделяя таким образом ключ от значения.
>>> for k, v in nums.items(): ... print(k, 'is', v) ... 1 is one 2 is two 3 is three 4 is four
Удаление записи из словаря выполняется с помощью встроенного оператора del
языка Python.
>>> e = {1.2: 5, 1.3: 10, 1.4: 8} >>> del e[1.2] >>> e {1.3: 10, 1.4: 8}
Если указанного ключа нет, то возникнет исключение KeyError
. В связи с этим интересен метод pop()
, который также удаляет элемент из словаря и также возбуждает KeyError
, если не передавать второй аргумент. Но если он указан, то в случае отсутствия ключа pop()
вернет этот дефолтный аргумент. Если же ключ есть, вернется его значение.
colors = {'white': 'ffffff', 'black': '000000', 'red': 'ff0000', 'green': '00ff00'} while colors: u = input('Цвет: ') print(colors.pop(u, '-')) print('Все цвета были использованы!')
Пример выполнения:
Цвет: white ffffff Цвет: black 000000 Цвет: green 00ff00 Цвет: dfdf - Цвет: red ff0000 Все цвета были использованы!
Примечание: при преобразовании пустого словаря к булевому значению возвращается False
. Поэтому цикл while
завершается, когда из словаря исчезают все записи.
Почитать о всех методах словаря можно в этой статье, также о разных способах создания словарей.
Практическая работа
-
Создайте словарь, связав его с переменной school, и наполните данными, которые бы отражали количество учащихся в разных классах (1а, 1б, 2б, 6а, 7в и т. п.). Внесите изменения в словарь согласно следующему: а) в одном из классов изменилось количество учащихся, б) в школе появился новый класс, с) в школе был расформирован (удален) другой класс. Вычислите общее количество учащихся в школе.
-
В Python ключи словаря можно получить, воспользовавшись встроенной функцией
list()
. Присвойте одной переменной произвольный словарь, второй ‒ список его ключей, полученный изlist()
, третьей ‒ результат выполнения словарного методаkeys()
. После этого внесите изменения в словарь, например, добавив в него еще одну запись. Выведите значения переменных на экран. Сделайте вывод об особенностях объектов типаdict_keys
.
Примеры решения и дополнительные уроки в pdf-версии курса