Словари (dict)

В Python'е есть еще один комплексный тип для работы с коллекциями, он называется "Словарь", он сочетает в себе возможности множеств и списков и так же имеет уникальные свойства. В этом уроке мы разберемся со словарями и научимся с ними работать.

Что общего у словарей и у списков:

  • Оба типа изменяемые (мутабельные);
  • Оба типа динамические, можно менять элементы, добавлять и удалять;
  • Могут содержать вложенные элементы, так же как список может содержать вложенный список словарь может быть словарем словарей (или списков)

Основное уникальное свойство словаря в отличии от списка: элементы словаря доступны по специальному ключу.

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

словарь = {
    'ключ1': значение,
    'ключ2': значение,
    ...
    'ключN': значение,
}

Давайте создадим словарь.

>>> stones = {'power': 'purple', 'space': 'blue', 'soul': 'orange', 'time': 'green', 'mind': 'yellow', 'reality': 'red',}
>>> stones
{'power': 'purple', 'space': 'blue', 'soul': 'orange', 'time': 'green', 'mind': 'yellow', 'reality': 'red'}
>>> type(stones)
<class 'dict'>

Можно так же создать словарь с помощью конструктора:

>>> dict(power='purple', space='blue')
{'power': 'purple', 'space': 'blue'}

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

>>> stones['time']
'green'

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

>>> stones['ego'] = 'white'
>>> stones
{'power': 'purple', 'space': 'blue', 'soul': 'orange', 'time': 'green', 'mind': 'yellow', 'reality': 'red', 'ego': 'white'}

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

>>> stones['ego'] = 'golden'
>>> stones['ego']
'golden'

Удаление элементов происходит похожим способом как и в списках:

>>> stones.pop('ego')
'golden'
>>> del stones['soul']
>>> stones
{'power': 'purple', 'space': 'blue', 'time': 'green', 'mind': 'yellow', 'reality': 'red'}

Полная очистка доступна при использовании метода clear().

При этом срезы со словарями не работают:

>>> stones[0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 0

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

>>> stones[0:2]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'slice'

Ограничения словарей

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

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

TypeError: unhashable type

Сообщение об ошибке ругалось на TypeError: unhashable type: 'slice'. Если погружаться в детали, то тут явно говорится, что не проблема в том, что мы попытались использовать в качестве ключа изменяемый тип. Тут явно говорится, что использован нехешируемый тип. Если вы хотите проверить возможно ли хешировать объект, то можете воспользовваться встроенной функцией hash():

>>> hash('infinity')
-3297352616845080135
>>> hash((1, 2, 3))
2528502973977326415
>>> hash(['r', 'g', 'b'])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

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

Порядок следования ключей в словарях

Начиная с версии Python 3.6 интерпретатор гарантирует порядок следования ключей в словарях.

Методы словаря

Метод Описание
clear() Полная очистка словаря
copy() Вернуть поверхностную копию словаря (1)
fromkeys(seq[, v]) Вернуть копию словаря с ключами seq и значениями v (None по умолчанию)
get(key[,d]) Получить значение ключа, если ключ не существует, то вернуть d (None по умолчанию)
items() Вернуть содержимое словаря в ввиде кортежа (key, value) (2)
keys() Получить список ключей (2)
pop(key[,d]) Вернуть значение ключа или d если ключа нет и удалить ключ. Если d не уазано и ключа нет KeyError.
popitem() Извлечь элмент и вернуть произвольный элемент в виде (ключ, значение). Вызывает исключение KeyError если словарь пуст
setdefault(key[,d]) Вернуть значение ключа, если ключа нет, то создать со значеним d и вернуть d (None по умолчанию).
update([other]) Обновить словарь значениями словаря other, новые значения добавляются, старые переписываются
values() Вернуть значения словаря списком (2)
  1. Поверхностная копия словаря состоит из ссылок на объекты, если объект изменится, то он изменится в обоих словарях. Это имеет значение для случаев со сложной вложенностью. Например:
>>> x = {'a':[1, 2, 3, ]}
>>> y = x.copy()
>>> x['a'][2] = 'xxx'
>>> y
{'a': [1, 2, 'xxx']}
  1. На самом деле возвращается специальный view объект.

Генератор словарей

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

>>> d = {x: x**2 for x in range(10)}
>>> d
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

Практические примеры работы со словарями

Словари достаточно часто используются в Python программах. Они удобны для работы со множеством видов данных:

  • Конфигурация проектов
  • Передача данных между разными частями программы
  • В тех случаях когда не известно будут ли доступны все ключи

И много других случаев.

Давайте рассмотрим несколько практических примеров.

Последовательное наполнение словаря

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

>>> hero = {}
>>> hero['name'] = "Тор"
>>> hero['side'] = "Добро"
>>> hero['full_name'] = "Тор Одинсон"
>>> hero['height'] = 198
>>> hero['weight'] = 290
>>> hero['authors'] = ["Стэн Ли", "Ларри Либер", "Джек Керби"]
>>> hero
{'name': 'Тор', 'side': 'Добро', 'full_name': 'Тор Одинсон', 'height': 198,
'weight': 290, 'authors': ['Стэн Ли', 'Ларри Либер', 'Джек Керби']}

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

Обновление словарей

Частая задача для программистов которые пишут программы в том, что для программ нужна конфигурация. Например если ваша программа считывает данные с какого-то сайта и на основе них делает отчет, то на первом этапе программист запишет переменную с адресом сайта в одну из переменных которые определить в начале программы, а адрес папки для вывода отчета в другую переменную. Но со временем программа может быть полезной не только на компьютере автора. Тут возникает необходимость создания какой-то переменной в которой будет храниться конфигурация программы и словарь подходит лучше всего. Удобно создать основную переменную в которой будут храниться все данные конфигурации по умолчанию и дать возможность пользователю самому определить новые значения тех ключей которые ему надо изменить, а потом воспользоваться методом .update() чтобы обновить только те ключи которые нужны.

В качестве примера программы

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# Базовая конфигурация
base = {
  'url': 'https://finance.yahoo.com/quote/EURUSD%3DX',
  'report_folder': '/reports',
  'title': 'Курс евро к доллару'
}
# Получаем новую конфигурацию

config = read_config()
# {'url': 'https://finance.yahoo.com/quote/EURCAD%3DX',
#  'title': 'Курс евро к канадскому доллару'}
base.update(config)
# Теперь новое значение base:
# {'url': 'https://finance.yahoo.com/quote/EURCAD%3DX',
#  'report_folder': '/reports',
#  'title': 'Курс евро к канадскому доллару'}

Работа со словарями как со множествми

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

>>> x = {'a': 1, 'b': 2, 'c': 3}
>>> y = {'c': 3, 'd': 4, 'e': 5}
>>> x.keys() - y.keys()
{'a', 'b'}
>>> x.keys() | y.keys()
{'e', 'c', 'a', 'b', 'd'}
>>> x.keys() & y.keys()
{'c'}
>>> x.items() & y.items()
{('c', 3)}

Ссылки