Отображения

Отображениями называются типы данных, поддерживающие оператор проверки на вхождение (in), функцию len() и возможность обхода элементов в цикле. Отображения – это коллекции пар элементов «ключ-значение», которые предоставляют методы доступа к элементам и их ключам и значениям. При выполнении итераций порядок следования элементов отображений может быть произвольным. В языке Python имеется два типа отображений: встроенный тип dict и тип collections.defaultdict, определяемый в стандартной библиотеке. Мы будем использовать термин словарь для ссылки на любой из этих типов, когда различия между ними не будут иметь никакого значения.

В качестве ключей словарей могут использоваться только хешируемые объекты, поэтому в качестве ключей словаря такие неизменяемые типы, как float, frozenset, int, str и tuple, использовать допускается, а изменяемые типы, такие как dict, list и set, – нет. С другой стороны, каждому ключу соответствует некоторое значение, которое может быть ссылкой на объект любого типа, включая числа, строки, списки, множества, словари, функции и т. д.

Словари могут сравниваться с помощью стандартных операторов сравнения (<, <=, ==, !=, >=, >), при этом сравнивание производится поэлементно (и рекурсивно, при наличии вложенных элементов, таких как кортежи или словари в словарях). Пожалуй, единственными операторами сравнения, применение которых к словарям имеет смысл, являются операторы == и !=.

Словари

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

Тип данных dict может вызываться как функция dict() – без аргументов она возвращает пустой словарь; если в качестве аргумента передается отображение, возвращается словарь, основанный на этом отображении: например, с аргументом типа dict возвращается поверхностная копия словаря. Существует возможность передавать в качестве аргумента последовательности, если каждый элемент последовательности в свою очередь является последовательностью из двух объектов, первый из которых используется в качестве ключа, а второй – в качестве значения. Как вариант, для создания словарей, в которых ключи являются допустимыми идентификаторами языка Python, можно использовать именованные аргументы; тогда имена аргументов будут играть роль ключей, а значения аргументов – роль значений ключей. Кроме того, словари могут создаваться с помощью фигурных скобок – пустые скобки {} создадут пустой словарь. Непустые фигурные скобки должны содержать один или более элементов, разделенных запятыми, каждый из которых состоит из ключа, символа двоеточия и значения. Еще один способ создания словарей заключается в использовании генераторов словарей – эта тема будет рассматриваться ниже, в соответствующем подразделе.

Ниже приводятся несколько способов создания словарей – все они создают один и тот же словарь:

d1 = dict({"id": 1948, "name": "Washer", "size": 3})
d2 = dict(id=1948, name="Washer", size=3)
d3 = dict([("id", 1948), ("name", "Washer"), ("size", 3)])
d4 = dict(zip(("id", "name", "size"), (1948, "Washer", 3)))
d5 = {"id": 1948, "name": "Washer", "size": 3}


Рисунок 5: Словарь – это неупорядоченная коллекция элементов (ключ, значение) с уникальными ключами

На рис. 5 демонстрируется словарь, созданный следующим фрагментом программного кода:

d = {"root": 18, "blue": [75, "R", 2], 21: "venus", -14: None,
     "mars": "rover", (4, 11): 18, 0: 45}

Ключи словарей являются уникальными, поэтому если в словарь добавляется пара «ключ-значение» с ключом, который уже присутствует в словаре, в результате происходит замена значения существующего ключа новым значением. Для доступа к отдельным элементам используются квадратные скобки: например, выражение d["root"] вернет 18, выражение d[21] вернет строку "venus", а выражение d[91] применительно к словарю, изображенному на рис. 5, возбудит исключение KeyError.

Квадратные скобки могут также использоваться для добавления и удаления элементов словаря. Чтобы добавить новый элемент, используется оператор =, например, d["X"] = 59. Для удаления элементов используется инструкция del, например, инструкция del d["mars"] удалит из словаря элемент с ключом "mars" или возбудит исключение KeyError, если элемент с указанным ключом отсутствует в словаре. Кроме того, элементы могут удаляться (и возвращаться вызывающей программе) методом dict.pop().

Словари поддерживают встроенную функцию len() и для ключей поддерживают возможность быстрой проверки на вхождение с помощью операторов in и not in. В табл. collections:maps:tbl:1 перечислены все методы словарей.

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

for item in d.items():
    print(item[0], item[1])

for key, value in d.items():
    print(key, value)

Таблица 3. Методы словарей

Синтаксис Описание
d.clear() Удаляет все элементы из словаря d
d.copy() Возвращает поверхностную копию словаря d
d.fromkeys(s, v) Возвращает словарь типа dict, ключами которого являются элементы последовательности s значениями либо None либо v, если аргумент v определен
d.get(k) Возвращает значение ключа k или None, если ключ k отсутствует в словаре
d.get(k, v) Возвращает значение ключа k или v, если ключ k отсутствует в словаре
d.items() Возвращает представление всех пар (ключ, значение) в словаре d
d.keys() Возвращает представление всех ключей словаря d
d.pop(k) Возвращает значение ключа k и удаляет из словаря элемент с ключом k или возбуждает исключение KeyError, если ключ k отсутствует в словаре
d.pop(k, v) Возвращает значение ключа k и удаляет из словаря элемент с ключом k или возвращает значение v, если ключ k отсутствует в словаре
d.popitem() Возвращает и удаляет произвольную пару (ключ, значение) из словаря d или возбуждает исключение KeyError, если словарь d пуст
d.setdefault(k, v) То же что и dict.get() за исключением того, что, если ключ k в словаре отсутствует, в словарь вставляется новый элемент с ключом k и со значением None или v, если аргумент v задан
d.update(a) Добавляет в словарь d пары (ключ, значение) из a, которые отсутствуют в словаре d а для каждого ключа который уже присутствует в словаре d выполняется замена соответствующим значением из a; a может быть словарем итерируемым объектом с парами (ключ значение) или именованными аргументами
d.values() Возвращает представление всех значений в словаре d

Обход значений в словаре выполняется похожим способом:

for value in d.values():
    print(value)

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

for key in d:
    print(key)

for key in d.keys():
    print(key)

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

for key in d:
    d[key] += 1

Методы dict.items(), dict.keys() и dict.values() возвращают представления словарей. Представление словаря – это в действительности итерируемый объект, доступный только для чтения и хранящий элементы, ключи или значения словаря в зависимости от того, какое представление было запрошено.

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

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

{keyexpression: valueexpression for key, value in iterable}
{keyexpression: valueexpression for key, value in iterable if condition}

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

file_sizes = {name: os.path.getsize(name) for name in os.listdir(".")}

Функция os.listdir() из модуля os («operating system» – операционная система) возвращает список файлов и каталогов в указанном каталоге, но при этом в список никогда не включаются специальные имена каталогов «.» или «..». Функция os.path.getsize() возвращает размер заданного файла в байтах. Чтобы отфильтровать каталоги и другие элементы списка, не являющиеся файлами, можно добавить дополнительное условие:

file_sizes = {name: os.path.getsize(name) for name in os.listdir(".")
	      if os.path.isfile(name)}

Функция os.path.isfile() из модуля os.path возвращает True, если указанный путь соответствует файлу, и False – в противном случае, то есть для каталогов, ссылок и тому подобного.

Генераторы словарей могут также использоваться для создания инвер- тированных словарей. Например, пусть имеется словарь d, тогда мы можем создать новый словарь, ключами которого будут значения словаря d, а значениями – ключи словаря d:

inverted_d = {v: k for k, v in d.items()}

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

Словари со значениями по умолчанию

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

При обращении к несуществующему («отсутствующему») ключу слова- ря возбуждается исключение KeyError. Это очень удобно, так как нередко для нас бывает желательно знать об отсутствии ключа, который, согласно нашим предположениям, может присутствовать. Но в некоторых случаях бывает необходимо, чтобы в словаре присутствовали все ключи, которые мы используем, даже если это означает, что элемент с заданным ключом добавляется в словарь в момент первого обращения к нему.

Например, допустим, что имеется словарь d, который не имеет элемента с ключом m, тогда выражение x = d[m] возбудит исключение KeyError. Если d – это словарь со значениями по умолчанию, созданный соответствующим способом, а элемент с ключом m принадлежит такому словарю, то при обращении к нему будет возвращено соответствующее значение, как и в случае с обычным словарем. Но если в словаре со значениями по умолчанию отсутствует ключ m, то будет создан новый элемент словаря с ключом m и со значением по умолчанию, и будет возвращено значение этого, вновь созданного элемента.