Что такое словарь в питоне. Интерактивный учебник языка Python

Словарь - неупорядоченная структура данных, которая позволяет хранить пары «ключ - значение». Вот пример словаря на Python:

Dictionary = {"персона": "человек", "марафон": "гонка бегунов длиной около 26 миль", "противостоять": "оставаться сильным, несмотря на давление", "бежать": "двигаться со скоростью"}

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

Gender_dict = {0: "муж", 1: "жен"}

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

Dictionary = {(1, 2.0): "кортежи могут быть ключами", 1: "целые числа могут быть ключами", "бежать": "строки тоже", ["носок", 1, 2.0]: "а списки не могут"}

Прим. перев. На самом деле проблема не с изменяемыми, а с нехэшируемыми типами данных, но обычно это одно и то же.

Получение данных из словаря

Для получения значения конкретного ключа используются квадратные скобки . Предположим, что в нашем словаре есть пара "марафон": 26 .

# берём значение с ключом "марафон" dictionary["марафон"]

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

Добавление и обновление ключей

Добавление новых пар в словарь происходит достаточно просто:

# Добавляем ключ "туфля" со значением "род обуви, закрывающей ногу не выше щиколотки" dictionary["туфля"] = "род обуви, закрывающей ногу не выше щиколотки"

Обновление существующих значений происходит абсолютно также:

# Обновляем ключ "туфля" и присваиваем ему значение "хорошая туфля" dictionary["туфля"] = "хорошая туфля"

Удаление ключей

Для удаления ключа и соответствующего значения из словаря можно использовать del

# Удаляем значение с ключом "противостоять" из словаря del dictionary["противостоять"]

Методы

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

Update

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

# Добавляем две пары в словарь dictionary, используя метод update dictionary.update({"бежал": "бежать в прошедшем времени", "туфли": "туфля во множественном числе"}) >>> dictionary {"марафон": "гонка бегунов длиной около 26 миль", "персона": "человек", "бежал": "бежать в прошедшем времени", "бежать": "двигаться со скоростью", "туфля": "род обуви, закрывающей ногу не выше щиколотки", "туфли": "туфля во множественном числе"}

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

Get

# Допустим, у нас есть словарь story_count story_count = {"сто": 100, "девяносто": 90, "двенадцать": 12, "пять": 5}

Метод get() возвращает значение по указанному ключу. Если указанного ключа не существует, метод вернёт None .

# Ключ "двенадцать" существует и метод get в данном случае вернёт 12 story_count.get("двенадцать")

Метод можно использовать для проверки наличия ключей в словаре:

>>> story_count.get("два") None

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

# Метод вернёт 0 в случае, если данного ключа не существует story_count.get("два", 0)

Pop

Метод pop() удаляет ключ и возвращает соответствующее ему значение.

>>> story_count.pop("девяносто") 90 >>> story_count {"двенадцать": 12, "сто": 100, "пять": 5}

Keys

Метод keys() возвращает коллекцию ключей в словаре.

>>> story_count.keys() ["сто", "пять", "двенадцать"]

Values

Метод values() возвращает коллекцию значений в словаре.

>>> story_count.values()

Items

Метод items() возвращает пары «ключ - значение».

>>> dictionary.items() [("персона", "человек"), ("бежать", "двигаться со скоростью"), ("туфля", "род обуви, закрывающей ногу не выше щиколотки"), ("бежал", "бежать в прошедшем времени"), ("марафон", "гонка бегунов длиной около 26 миль"), ("туфли", "туфля во множественном числе")]

Итерация через словарь

Вы можете провести итерацию по каждому ключу в словаре.

Серия контента:

После списков словарь является самым гибким встроенным типом. Если список - это упорядоченная коллекция, то словарь - неупорядоченная. Основные особенности словарей:

  1. Доступ осуществляется по ключу, а не по индексу. По аналогии со списком, в словаре можно получить доступ к элементам в цикле по ключам.
  2. Значения словаря хранятся в неотсортированном порядке, более того, ключи могут храниться не в том порядке, в котором они добавляются.
  3. По аналогии со списками, словарь может хранить вложенные словари. Словарь может хранить в качестве значений объекты любого типа (heterogeneous). Ключ в словаре - immutable тип, может быть строкой, целым числом, float либо кортежем, состоящим из указанных типов.
  4. Словари реализованы как хеш-таблицы с быстрым доступом.
  5. Словари, так же как и списки, хранят ссылки на объекты, а не сами объекты.

Сегодня мы рассмотрим следующие темы.

  1. Что такое словарь.
  2. Функции/методы словаря.
  3. Операции со словарем.
  4. Примеры.

1. Что такое словарь

Словарь (dictionary) - это ассоциативный массив или хеш. Это неупорядоченное множество пар ключ: значение с требованием уникальности ключей. Пара фигурных скобок {} создает пустой словарь. В отличие от последовательностей, доступ к элементам словаря производится по ключу, а не по индексу, ключ может быть любого типа, ключ не допускает изменений.

Основные операции над словарем - сохранение с заданным ключом и извлечение по нему значения. Также можно удалить пару key: value с помощью инструкции del .

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

Пример - словарь в качестве телефонного справочника:

>>> dic = {"vanya" : 23323223, "smith" : 32232332} >>> dic["fedya"] = 33332222 >>> dic {"vanya": 23323223, "fedya": 33332222, "smith": 32232332} >>> dic["smith"] 32232332 >>> del dic["vanya"] >>> dic {"fedya": 33332222, "smith": 32232332} >>> dic.keys() ["fedya", "smith"] >>> dic.has_key("fedya") True

Создать словарь можно несколькими способами:

  1. Обычное выражение - оно удобно, если словарь статичен: D = {"name": "mel", "age": 45}
  2. Динамический вариант создания на лету: D = {} D["name"] = "mel" D["age"] = 45
  3. С помощью функции dict() - ключи при этом должны быть строками. С помощью этой функции можно избавить себя от обязательного условия заключать ключ в кавычки. В примере приведены четыре варианта создания одного и того же словаря: 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)))
  4. С помощью fromkeys() - создает словарь по списку ключей с пустыми значениями: D = {}.fromkeys(["name", "age"],123)
  5. С помощью конструктора: d = dict((x, x**2) for x in xrange(5))

2. Функции/методы словаря

dict() - создание словаря;

len() - возвращает число пар;

clear() - удаляет все значения из словаря;

copy() - создает псевдокопию словаря;

deepcopy() - создает полную копию словаря;

fromkeys() - создание словаря;

get() - получить значение по ключу;

has_key() - проверка значения по ключу;

items() - возвращает список значений;

iteriyems() - возвращает итератор;

iterkeys() - возвращает итератор ключей;

pop() - извлекает значение по ключу;

popitem() - извлекает произвольное значение;

update() - изменяет словарь;

values() - возвращает список значений;

itervalues() - возвращает итератор на список значений.

in - оператор, проверяет наличие значения по ключу;

del - оператор, удаляет пару по ключу;

dict() - конструирует словарь с помощью последовательности.

Например, создать словарь с помощью списка кортежей:

>>> items = [("name","sveta"),("age",20)] >>> d = dict(items) >>> d {"age": 20, "name": "sveta"} >>> len(d) 2

in() - оператор проверки вхождения.

Пример: база данных может быть заполнена в виде словаря.

Проверить наличие в базе данных телефона по имени:

people = {"Alice": {"phone": "2341", "addr": "Foo drive 23" }, "Beth": {"phone": "9102", "addr": "Bar street 42"}} name = "Alice" key = "phone" if name in people: print "%s phone is %s" % (name, people) >>> Alice phone is 2341 copy()

Пример создания копии словаря:

>>> x = {"user":"admin","attr":} >>> y = x.copy() >>> y {"user": "admin", "attr": }

Метод copy() не делает полного копирования: если мы, например, сделаем операцию:

>>> x["attr"].remove(1)

то с удивлением обнаружим, что удаление атрибута произойдет также и в копии.

Чтобы этого не произошло, нужно использовать метод deepcopy() .

>>> from copy import deepcopy >>> y = x.deepcopy()

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

>>> {}.fromkeys(["name", "age"]) {"age": None, "name": None}

Можно все значения заполнить по умолчанию:

>>> {}.fromkeys(["name", "age"],123) {"age": 123, "name": 123}

get() - получает значение по ключу, в случае отсутствия дает None:

>>> d = {} >>> print d.get("name") None

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

>>> d = {} >>> d.has_key("name") False

items() - возвращает список значений:

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

iteriyems() - возвращает итератор - выдает тот же результат:

>>> for k, v in d.iteritems(): ... print k, v

keys() - возвращает список ключей;

iterkeys() - возвращает итератор ключей:

>>> d.keys() ["url", "title"] >>> d.iterkeys()

pop() - извлекает значение по ключу с последующим удалением:

>>> d.pop("title") >>> d {"url": "http://www.python.org"}

popitem() - извлекает произвольное значение с последующим удалением:

>>> d = {"title": "Python Web Site", "url": "http://www.python.org", "www": "python"} >>> d.popitem() >>> d {"www": "python", "title": "Python Web Site"}

update() - изменяет значение по ключу:

>>> d2 = {"www":"python.org"} >>> d.update(d2) >>> d {"www": "python.org", "title": "Python Web Site"}

values() - возвращает список значений:

>>> d={} >>> d=1 >>> d=2 >>> d=3 >>> d {1: 1, 2: 2, 3: 3} >>> d.values()

del - оператор удаляет пару ключ: значение по ключу:

>>> del d >>> d {1: 1, 3: 3}

3. Операции

Поскольку словари представляют собой мапы (map), а не последовательности, к ним нельзя применить конкатенацию или срезы.

К словарям можно применять стандартные операторы сравнения:

<, <=, ==, !=, >=, >

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

>>> table = {"Python": "Guido van Rossum", ... "Perl": "Larry Wall", ... "Tcl": "John Ousterhout" } >>> for lang in table: ... print(lang, table) .. >>> Tcl John Ousterhout >>> Python Guido van Rossum >>> Perl Larry Wall

Словари хорошо подходят для хранения многомерных массивов или матриц:

>>> Matrix = {} >>> Matrix[(2, 3, 4)] = 88 >>> Matrix[(7, 8, 9)] = 99 >>> >>> X = 2; Y = 3; Z = 4 >>> Matrix[(X, Y, Z)] 88 >>> Matrix {(2, 3, 4): 88, (7, 8, 9): 99}

С помощью словарей можно хранить структурированную информацию в виде записей:

>>> man = {"name": "Serg", ... "jobs": ["programmer", "writer"], ... "web": "www.iakovlev.org", ... "home": {"city": "Moscow", "zip":129000}} >>> man["name"] Serg >>> man["jobs"] "writer"

4. Примеры

Пример 1. Подсчитаем, сколько раз в строке встречается каждый символ:

def histogram(s): d = dict() for c in s: if c not in d:d[c] = 1 else:d[c] += 1 return d hist = histogram("how many times") >>> {"a": 1,"e": 1,"i": 1,"h": 1,"m": 2,"o": 1,"n": 1,"s": 1,"t": 1,"w": 1,"y": 1}

Если нам нужно инвертировать данный словарь и в качестве ключа поставить частоту:

def invert_dict(d): inv = dict() for key in d: val = d if val not in inv:inv = else:inv.append(key) return inv print invert_dict(hist) >>> {1: ["a", "e", "i", "h", "o", "n", "s", "t", "w", "y"], 2: [" ", "m"]} import string import sys words = {} strip = string.whitespace + string.punctuation + string.digits + "\""" filename = "file" for line in open(filename): for word in line.lower().split(): word = word.strip(strip) if len(word) > 2: words = words.get(word, 0) + 1 for word in sorted(words): print(""{0}" occurs {1} times".format(word, words))

Пример 3. Сортировка словаря по ключам:

author = {"php":"Rasmus Lerdorf",\ "perl":"Larry Wall",\ "tcl":"John Ousterhout",\ "awk":"Brian Kernighan",\ "java":"James Gosling",\ "parrot":"Simon Cozens",\ "python":"Guido van Rossum"} #Либо так: langs = author.keys() langs.sort() for language in langs: print language," - ",author #либо так: for key in sorted(author.iterkeys()): print "%s: %s" % (key, author) >>> awk - Brian Kernighan >>> java - James Gosling >>> parrot - Simon Cozens >>> perl - Larry Wall >>> php - Rasmus Lerdorf >>> python - Guido van Rossum >>> tcl - John Ousterhout

Пример 4. Как инвертировать словарь, т.е. поменять ключи со значениями:

def invert_dict_nonunique(d): newdict = {} for k, v in d.iteritems(): newdict.setdefault(v, ).append(k) return newdict d = {"child1": "parent1","child2": "parent1","child3": "parent2","child4": "parent2"} print invert_dict_nonunique(d) >>> {"parent2": ["child3", "child4"], "parent1": ["child1", "child2"]}

Заключение

Можно подвести итоги: словари наряду со списками являются наиболее простыми, гибкими и мощными коллекционными типами. Словарь, как и список, является изменяемым (mutable) типом данных, хотя и содержит неизменяемые ключи и может неограниченно расти. Если вам нужна коллекция с доступом по ключу - словарь подходит для этого лучше всего. Если вам нужна коллекция для хранения произвольных объектов произвольной вложенности - словарь в этом вам поможет.

Код примеров проверялся на версии питона 2.6.

Ресурсы для скачивания

static.content.url=http://www.сайт/developerworks/js/artrating/

Zone=Open source, Linux

ArticleID=505647

ArticleTitle=Программирование на Python: Часть 4. Словари

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

Можно выделить несколько отличительных характеристик словарей:

  1. Для доступа к ним используется не индекс, а ключ. Аналогично спискам, в словарях есть возможность получения доступа к элементам цикла по ключам.
  2. Для хранения словарей используется неотсортированный порядок, кроме того, допускается сохранение ключей в порядке, отличном от порядка их добавления.
  3. Аналогично список, в словаре могут быть вложенные словари. Значениями словаря могут быть объекты любого типа (heterogeneous). Ключ словаря – immutable тип может являться float, целым числом, строкой или кортежем, включающим указанные типы.
  4. Словарь имеет вид хеш-таблицы быстрого доступа.
  5. Аналогично спискам, словари не хранят непосредственно объекты, а только ссылки на них.

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

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

С словарем проводится две основные операции – сохранение с указанным ключом, а также извлечение значения по нему. Инструмент del дает возможность удаления пары key: value.

Метод keys () для словарей используется с целью возвращения списка всех применяемых ключей в произвольном порядке. Чтобы отсортировать список необходимо пользоваться методом sort (). Чтобы определить наличие конкретного ключа следует пользоваться методом has key(), однако в 3-й версии он уже устаревший и вместо него следует пользоваться оператором in. Чтобы добавить в словарь новый объект не нужно проводить предварительные проверки: если у ключа дор этого уже было определенное значение, то произойдет его перезапись.

Как пример возьмем работу с электронным досье Васи Пупкина.

# создадим пустой словарь data = {} # или так data = dict() # определим его длину - 0 len(data) # заполним данными - имя и фамилия data = {"firstname": "Vasya", "lastname": "Pupkin"} # длина словаря определяется количеством ключей # на данный момент - 2 len(data) # добавим отчество data["middlename"] = "Vasilyevich" # после женитьбы Васи обновляем поле фамилии и добавляем банковский счет data.update({"lastname":"Gates", "bank_account": 10000000}) # добавляем новым элементом словарь с данными жены... data["wife"] = {"firstname": "Annet", "lastname": "Gates", "middlename": "Billovna"} # ... и загоняем в гараж приданое data["garage"] = ["Jaguar", "Toyota Camry"] # и еще одну машинку data["garage"].append("Honda Civic") # узнаем имя жены - Annet print data["wife"]["firstname"] # Вася хвастается друзьям print data["bank_account"] # если нет элемента с нужным ключом, # можно избежать ошибки if "bank_account" in data: print data["bank_account"] else: print "no money" # или так print data.get("bank_account", 0) # закрываем счет del data["bank_account"] # распечатываем досье for key in data: print key, ":", data #### после распечатки выдаст следующее # firstname: Vasya # wife: {"middlename": "Billovna", "lastname": "Gates", "firstname": "Annet"} # middlename: Vasilyevich # lastname: Gates # garage: ["Jaguar", "Toyota Camry", "Honda Civic"] #### а само досье выглядит так # {"firstname": "Vasya", "wife": {"middlename": "Billovna", "lastname": "Gates", # "firstname": "Annet"}, "middlename": "Vasilyevich", "lastname": "Gates", # "garage": ["Jaguar", "Toyota Camry", "Honda Civic"]}

На этом, пожалуй, все.

На Stack Overflow.
В статье рассматривается реализация CPython версии 2.7. Все примеры были подготовлены в 32-битной версии Python на 64-битной ОС, для другой версии значения будут отличаться.

Словарь в Python

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

Инициализация и добавление элементов:

>>> d = {} # то же самое, что d = dict() >>> d["a"] = 123 >>> d["b"] = 345 >>> d["c"] = 678 >>> d {"a": 123, "c": 678, "b": 345}
Получение элемента:

>>> d["b"] 345
Удаление элемента:

>>> del d["c"] >>> d {"a": 123, "b": 345}
Ключами словаря могут быть значения только hashable типов, то есть типов, у которых может быть получен хэш (для этого у них должен быть метод __hash__()). Поэтому значения таких типов, как список (list), набор (set) и собственно сам словарь (dict), не могут быть ключами словаря:

>>> d = 1 Traceback (most recent call last): File "", line 1, in TypeError: unhashable type: "list" >>> d = 2 Traceback (most recent call last): File "", line 1, in TypeError: unhashable type: "set" >>> d = 3 Traceback (most recent call last): File "", line 1, in TypeError: unhashable type: "dict"

Реализация

Словарь в Python реализован в виде хэш-таблицы. Как известно, реализация хэш-таблицы должна учитывать возможность появления коллизий – ситуаций, когда разные ключи имеют одинаковое значение хэша. Должен быть способ вставки и извлечения элементов с учётом коллизий. Существует несколько способов разрешения коллизий, например метод цепочек и метод открытой адресации. В Python используется метод открытой адресации. Разработчики предпочли метод открытой адресации методу цепочек ввиду того, что он позволяет значительно сэкономить память на хранении указателей, которые используются в хэш-таблицах с цепочками.

В рассматриваемой реализации каждая запись (PyDictEntry) в хэш-таблице словаря состоит из трёх элементов – хэш, ключ и значение.

Typedef struct { Py_ssize_t me_hash; PyObject *me_key; PyObject *me_value; } PyDictEntry;
Структура PyDictObject выглядит следующим образом:

#define PyDict_MINSIZE 8 typedef struct _dictobject PyDictObject; struct _dictobject { PyObject_HEAD Py_ssize_t ma_fill; Py_ssize_t ma_used; Py_ssize_t ma_mask; PyDictEntry *ma_table; PyDictEntry *(*ma_lookup)(PyDictObject *mp, PyObject *key, long hash); PyDictEntry ma_smalltable; };
При создании нового объекта словаря его размер равен 8. Это значение определяется константой PyDict_MINSIZE. Для хранения хэш-таблицы в PyDictObject были введены переменные ma_smalltable и ma_table. Переменная ma_smalltable с предвыделенной памятью размером PyDict_MINSIZE (то есть 8) позволяет небольшим словарям (до 8 объектов PyDictEntry) храниться без дополнительного выделения памяти. Эксперименты, проведённые разработчиками, показали, что этого размера вполне достаточно для большинства словарей: небольших словарей экземпляров и обычно небольших словарей, созданных для передачи именованных аргументов (kwargs). Переменная ma_table соответствует ma_smalltable для маленьких таблиц (то есть для таблиц из 8 ячеек). Для таблиц большего размера память ma_table выделяется отдельно. Переменная ma_table не может быть равна NULL.

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

Ячейка в хэш-таблице может иметь три состояния

1) Неиспользованная (me_key == me_value == NULL)
Данное состояние означает, что ячейка не содержит и ещё никогда не содержала пару (ключ, значение).
После вставки ключа «неиспользованная» ячейка переходит в состояние «активная».
Это состояние – единственный случай, когда me_key = NULL и является начальным состоянием для всех ячеек в таблице.
2) Активная (me_key != NULL и me_key != dummy и me_value != NULL)
Означает, что ячейка содержит активную пару (ключ, значение).
После удаления ключа ячейка из состояния «активная» переходит в состояние «пустая» (то есть me_key = dummy, а
dummy = PyString_FromString("")).
Это единственное состояние, когда me_value != NULL.
3) Пустая (me_key == dummy и me_value == NULL)
Это состояние говорит о том, что ячейка ранее содержала активную пару (ключ, значение), но она была удалена, и новая активная пара ещё не записана в ячейку.
Так же как и при состоянии «неиспользованная», после вставки ключа ячейка из состояния «пустая» переходит в состояние «активная».
«Пустая» ячейка не может вернуться в состояние «неиспользованная», то есть вернуть me_key = NULL, так как в данном случае последовательность проб в случае коллизии не будет иметь возможность узнать, были ли ячейки «активны».

Переменные-члены структуры

ma_fill – число ненулевых ключей (me_key != NULL), то есть сумма «активных» и «пустых» ячеек.
ma_used – число ненулевых и не «пустых» ключей (me_key != NULL, me_key != dummy), то есть число «активных» ячеек.
ma_mask – маска, равная PyDict_MINSIZE - 1.
ma_lookup – функция поиска. По умолчанию при создании нового словаря используется lookdict_string. Так сделано потому, что разработчики посчитали, что большинство словарей содержат только строковые ключи.

Основные тонкости

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

>>> map(hash, ) >>> map(hash, ["abca", "abcb", "abcc", "abcd", "abce"])
Для целых чисел хэшами являются их значения, поэтому подряд идущие числа будут иметь подряд идущие хэши, а для строк подряд идущие строки имеют практически подряд идущие хэши. Это не обязательно плохо, напротив, в таблице размером 2**i взятие i младших бит хэша как начального индекса таблицы выполняется очень быстро, и для словарей, проиндексированных последовательностью целых чисел, коллизий не будет вообще:

То же самое будет почти полностью соблюдаться, если ключи словаря – это «подряд идущие» строки (как в примере выше). В общих случаях это дает более чем случайное поведение, что и требуется.

Взятие только i младших бит хэша в качестве индекса также уязвимо к коллизиям: например, возьмём список в качестве набора ключей. Так как целые являются их собственными хэшами и это вписывается в словарь размера 2**15 (так как 20000 < 32768), последние 15 бит от каждого хэша все будут равны 0.

>>> map(lambda x: "{0:016b}".format(x), ) ["0000000000000000", "10000000000000000", "100000000000000000", "110000000000000000", "1000000000000000000", "1010000000000000000", "1100000000000000000", "1110000000000000000", ...]
Получится, что все ключи будут иметь один и тот же индекс. То есть для всех ключей (кроме первого) произойдут коллизии.
Примеры специально подобранных «плохих» случаев не должны влиять на обычные случаи, так что просто оставим взятие последних i бит. Всё остальное отдаётся на откуп методу разрешения коллизий.

Метод разрешения коллизий

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

Обычно ячейка находится с первой попытки (и это действительно так, ведь коэффициент заполнения хэш-таблицы держится ниже 2/3), что позволяет не тратить время на пробирование и делает расчет начального индекса очень быстрым. Но давайте рассмотрим, что случится, если произойдёт коллизия.

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

J = ((5 * j) + 1) % 2**i
Для любого начального j в пределах вызов данной формулы 2**i раз вернёт каждое число в пределах ровно один раз. Например:

>>> j = 0 >>> i = 3 >>> for _ in xrange(0, 2**i): ... print j, ... j = ((5 * j) + 1) % 2**i ... 0 1 6 7 4 5 2 3
Вы скажете, что это ничем не лучше использования линейного пробирования с постоянным шагом, ведь в данном случае ячейки в хэш-таблице также просматриваются в определенном порядке, но это не единственное отличие. В общих случаях, когда хэш значения ключей идут подряд, данный метод лучше линейного пробирования. Из примера выше можно проследить, что для таблицы размером 8 (2**3) порядок индексов будет следующим:

0 -> 1 -> 6 -> 7 -> 4 -> 5 -> 2 -> 3 -> 0 -> … (затем идут повторения)
Если произойдёт коллизия для пробы с индексом, равным 5, то следующий индекс пробы будет 2, а не 6, как в случае линейного пробирования с шагом +1, поэтому для ключа, добавляемого в будущем, индекс пробы которого будет равен 6, коллизии не произойдёт. Линейное пробирование в данном случае (при последовательных значениях ключей) было бы плохим вариантом, так как происходило бы много коллизий. Вероятность же того, что хэш значения ключей будут идти в порядке 5 * j + 1, намного меньше.

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

J = (5 * j) + 1 + perturb perturb >>= PERTURB_SHIFT затем j % 2**i используется как следующий индекс пробы где: perturb = hash(key) PERTURB_SHIFT = 5
После этого последовательность проб будет зависеть от каждого бита хэша. Псевдослучайное изменение очень эффективно, потому что быстро увеличивает различия в битах. Так как переменная perturb – беззнаковая, то, если пробирование выполняется достаточно часто, переменная perturb в конечном итоге становится и остаётся равной нулю. В этот момент (который достигается очень редко) результат j снова становится равен 5 * j + 1. Далее поиск происходит точно так же, как в первой части метода, и свободная ячейка в конечном счете будет найдена, поскольку, как было сказано ранее, каждое число в диапазоне будет возвращено ровно один раз, и мы уверены, что всегда есть по крайней мере одна «неиспользованная» ячейка.

Выбор «хорошего» значения для PERTURB_SHIFT – это вопрос балансировки. Если сделать его малым, то старшие биты хэша будут влиять на последовательность проб по итерациям. Если же сделать его большим, то в действительно «плохих» случаях старшие биты хэша будут оказывать влияние только на ранних итерациях. В результате экспериментов, которые провёл один из разработчиков Python – Тим Питерс, значение PERTURB_SHIFT было выбрано равным 5, так как это значение оказалось «лучшим». То есть показало минимальное общее число коллизий как для нормальных, так и для специально подобранных «плохих» случаев, хотя значения 4 и 6 не были значительно хуже.

Историческая справка: Один из разработчиков Python, Реймер Берендс, предлагал идею использования подхода расчёта индексов таблицы, основанного на многочленах, который затем был улучшен Кристианом Тисмером. Этот подход также показал отличные результаты по возникновению коллизий, но требовал больше операций, а также дополнительной переменной для хранения многочлена таблицы в структуре PyDictObject. В экспериментах Тима Питерса текущий, используемый в Python метод оказался быстрее, показывая в равной степени хорошие результаты по возникновению коллизий, но требовал меньше кода и использовал меньше памяти.

Инициализация словаря

Когда вы создаёте словарь, вызывается функция PyDict_New. В этой функции последовательно выполняются следующие операции: происходит выделение памяти для нового объекта словаря PyDictObject. Переменная ma_smalltable очищается. Переменные ma_used и ma_fill приравниваются 0, ma_table становится равной ma_smalltable. Значение ma_mask приравнивается PyDict_MINSIZE - 1. Функция для поиска ma_lookup делается равной lookdict_string. Возвращается созданный объект словаря.

Добавление элемента

При добавлении элемента в словарь или изменении значения элемента в словаре вызывается функция PyDict_SetItem. В этой функции берётся значение хэша и вызывается функция insertdict, а также функция dictresize, если таблица заполняется на 2/3 от текущего размера.

В свою очередь в функции insertdict происходит вызов lookdict_string (или lookdict, если в словаре есть не строковый ключ), в которой происходит поиск свободной ячейки в хэш-таблице для вставки. Эта же функция используется для нахождения ключа при извлечении.

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

>>> PyDict_MINSIZE = 8 >>> key = 123 >>> hash(key) % PyDict_MINSIZE >>> 3
В Python это реализовано с использованием логической операции «И» и маски. Маска равна следующему значению: mask = PyDict_MINSIZE - 1.

>>> PyDict_MINSIZE = 8 >>> mask = PyDict_MINSIZE - 1 >>> key = 123 >>> hash(key) & mask >>> 3
Так получаются младшие биты хэша:
2**i = PyDict_MINSIZE, отсюда i = 3, то есть достаточно трёх младших бит.
hash(123) = 123 = 1111011 2
mask = PyDict_MINSIZE - 1 = 8 - 1 = 7 = 111 2
index = hash(123) & mask = 1111011 2 & 111 2 = 011 2 = 3

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

Это объясняет хитрый момент, связанный с добавлением равных по значению, но разных по типу ключей (к примеру, float, int и complex):

>>> 7.0 == 7 == (7+0j) True >>> d = {} >>> d="float" >>> d {7.0: "float"} >>> d="int" >>> d {7.0: "int"} >>> d="complex" >>> d {7.0: "complex"} >>> type(d.keys())
То есть тот тип, который был добавлен в словарь первым, и будет типом ключа, несмотря на обновление. Это объясняется тем, что реализация хэша для float значений возвращает хэш от int, если дробная часть равна 0.0. Пример расчёта хэша для float, переписанный на Python:

Def float_hash(float_value): ... fractpart, intpart = math.modf(float_value) if fractpart == 0.0: return int_hash(int(float_value)) # использовать хэш int ...
А хэш от complex возвращает хэш от float. В данном случае возвращается хэш только действительной части, так как мнимая часть равна нулю:

Def complex_hash(complex_value): hashreal = float_hash(complex_value.real) if hashreal == -1: return -1 hashimag = float_hash(complex_value.imag) if hashimag == -1: return -1 res = hashreal + 1000003 * hashimag res = handle_overflow(res) if res == -1: res = -2 return res
Пример:

>>> hash(7) 7 >>> hash(7.0) 7 >>> hash(7+0j) 7
Ввиду того, что и хэши, и значения для всех трёх типов равны, выполняется простое обновление значения найденной записи.

Замечание по поводу добавления элементов: Python запрещает добавление элементов в словарь во время итерации, поэтому при попытке добавить новый элемент произойдёт ошибка:

>>> d = {"a": 1} >>> for i in d: ... d["new item"] = 123 ... Traceback (most recent call last): File "", line 1, in RuntimeError: dictionary changed size during iteration
Вернёмся к процедуре добавления элемента в словарь. После успешного добавления или обновления записи в хэш-таблице происходит сравнение следующей записи-кандидата на вставку. Если хэш или ключ у записей не совпадают, начинается пробирование. Происходит поиск «неиспользованной» ячейки для вставки. В данной реализации Python используется случайное (а если переменная perturb равна нулю – квадратичное) пробирование. Как было описано выше, при случайном пробировании индекс следующей ячейки выбирается псевдослучайным образом. Запись добавляется в первую найденную «неиспользованную» ячейку. То есть два ключа a и b, у которых hash(a) == hash(b), но a != b могут легко существовать в одном словаре. В случае если ячейка по начальному индексу пробы «пустая», произойдёт пробирование. И если первая найденная ячейка будет «нулевая», то «пустая» ячейка будет использована заново. Это позволяет перезаписать удалённые ячейки, сохраняя ещё неиспользованные.

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

>>> d1 = {"one": 1, "two": 2, "three": 3, "four": 4, "five": 5} >>> d2 = {"three": 3, "two": 2, "five": 5, "four": 4, "one": 1} >>> d1 == d2 True >>> d1.keys() ["four", "three", "five", "two", "one"] >>> d2.keys() ["four", "one", "five", "three", "two"]
Это объясняет, почему словари в Python при выводе содержимого отображают хранимые пары (ключ, значение) не в порядке их добавления в словарь. Словари выводят их в порядке расположения в хэш-таблице (то есть в порядке индексов).

Рассмотрим пример:

>>> d = {} >>> d["habr"] = 1

Произошла вставка по индексу 5. Переменные ma_fill и ma_used стали равны 1.

>>> d["python"] = 2

Произошла вставка по индексу 0. Переменные ma_fill и ma_used стали равны 2.

>>> d["dict"] = 3

Произошла вставка по индексу 4. Переменные ma_fill и ma_used стали равны 3.
>>> d["article"] = 4

Произошла вставка по индексу 1. Переменные ma_fill и ma_used стали равны 4.

>>> d["!!!"] = 5

Произошло следующее:
hash("!!!") = -1297030748
i = -1297030748 & 7 = 4
Но как видно из таблицы, индекс 4 уже занят записью с ключом "dict". То есть произошла коллизия. Начинается пробирование:
perturb = -1297030748
i = (i * 5) + 1 + perturb
i = (4 * 5) + 1 + (-1297030748) = -1297030727
index = -1297030727 & 7 = 1
Новый индекс пробы равен 1, но данный индекс тоже занят (записью с ключом "article"). Произошла ещё одна коллизия, продолжаем пробирование:
perturb = perturb >> PERTURB_SHIFT
perturb = -1297030748 >> 5 = -40532211
i = (i * 5) + 1 + perturb
i = (-1297030727 * 5) + 1 + (-40532211) = -6525685845
index = -6525685845 & 7 = 3
Новый индекс пробы равен 3, и, так как он не занят, происходит вставка записи с ключом "!!!" в ячейку с третьим индексом. В данном случае запись была добавлена после двух проб из-за произошедших коллизий. Переменные ma_fill и ma_used стали равны 5.

>>> d {"python": 2, "article": 4, "!!!": 5, "dict": 3, "habr": 1}
Пробуем добавить шестой элемент в словарь.

>>> d[";)"] = 6

После добавления шестого элемента таблица будет заполнена на 2/3, а соответственно, произойдёт изменение её размера. После того как размер изменится (в данном случае увеличится в 4 раза), произойдёт полная перестройка хэш-таблицы с учётом нового размера – все «активные» ячейки будут перераспределены, а «пустые» и «неиспользованные» ячейки будут проигнорированы.

Размер хэш-таблицы теперь равен 32, а переменные ma_fill и ma_used стали равны 6. Как видно, порядок элементов полностью поменялся:

>>> d {"!!!": 5, "python": 2, "habr": 1, "dict": 3, "article": 4, ";)": 6}

Поиск элемента

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

>>> d = {"a": 123, "b": 345, "c": 678} >>> d["x"] Traceback (most recent call last): File "", line 1, in KeyError: "x"

Коэффициент заполнения хэш-таблицы

Размер таблицы изменяется, когда она заполняется на 2/3. То есть для начального размера хэш-таблицы словаря, равного 8, изменение произойдёт после того, как будет добавлен 6 элемент.

>>> 8 * 2.0 / 3 5.333333333333333
При этом происходит перестройка хэш-таблицы с учётом её нового размера, а соответственно, меняются и индексы всех записей.

Значение 2/3 от размера было выбрано как оптимальное, для того чтобы пробирование не занимало слишком много времени, то есть вставка новой записи происходила быстро. Увеличение этого значения приводит к тому, что словарь плотнее заполняется записями, что в свою очередь увеличивает количество коллизий. Уменьшение же увеличивает разреженность записей в ущерб увеличения занимаемых ими кэш-линий процессора и в ущерб увеличения общего объема памяти. Проверка заполнения таблицы происходит в весьма чувствительном ко времени участке кода. Попытки сделать проверку более сложной (например, изменяя коэффициент заполнения для разных размеров хэш-таблицы) уменьшали производительность.

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

>>> d = dict.fromkeys(range(5)) >>> d.__sizeof__() 248 >>> d = dict.fromkeys(range(6)) >>> d.__sizeof__() 1016
В примере возвращается размер всего объекта PyDictObject для 64-битной версии ОС.
Начальный размер таблицы равен 8. Таким образом, когда число заполненных ячеек будет равно 6 (то есть больше 2/3 от размера), размер таблицы увеличится до 32. Затем, когда число будет равно 22, увеличится до 128. При 86 увеличится до 512, при 342 – до 2048 и так далее.

Коэффициент увеличения размера таблицы

Коэффициент увеличения размера таблицы при достижении максимального уровня заполнения равен 4, если размер таблицы меньше 50000 элементов, и 2 – для таблиц большего размера. Такой подход может быть полезен приложениям с ограничениями по памяти.

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

Обычно добавление элемента в словарь может увеличить его размер в 4 или 2 раза в зависимости от текущего размера словаря, но также возможно, что размер словаря уменьшится. Такая ситуация может произойти, если ma_fill (количество ненулевых ключей, сумма «активных» и «пустых» ячеек) намного больше ma_used (количество «активных» ячеек), то есть много ключей было удалено.

Удаление элемента

При удалении элемента из словаря вызывается функция PyDict_DelItem.
Удаление из словаря происходит по ключу, хотя в действительности освобождения памяти не происходит. В этой функции вычисляется хэш значение ключа, а затем происходит поиск записи в хэш-таблице с использованием всё той же функции lookdict_string или lookdict. В случае если запись с таким ключом и хэшем найдена, ключ этой записи выставляется в состояние «пустой» (то есть me_key = dummy), а значение записи – в NULL (me_value = NULL). После этого переменная ma_used уменьшится на единицу, а ma_fill останется без изменения. Если же запись не найдена, возвращается ошибка.

>>> del d["!!!"]

После удаления переменная ma_used стала равна 4, а ma_fill осталась равной 5, так как ячейка не была удалена, а всего лишь была помечена как «пустая» и продолжает занимать ячейку в хэш-таблице.

Рандомизация хэшей

При запуске python можно воспользоваться ключом -R, чтобы использовать псевдослучайную «соль». В этом случае хэш значения таких типов, как строки, buffer, bytes, и объекты datetime (date, time и datetime) будут непредсказуемыми между вызовами интерпретатора. Данный способ предложен в качестве защиты от DoS атак.

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

Создание

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

A = {1: "one", 2: "two", 3: "three"} print(a) {1: "one", 2: "two", 3: "three"}

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

A = dict(one = 1, two = 2, three = 3) print(a) {"one": 1, "two": 2, "three": 3}

Как и в прошлый раз, функция print отображает содержимое словаря a. В данном случае имеется пары объектов, представленных также в виде чисел и строк.

Добавление элемента

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

A = {1: "one", 2: "two", 3: "three"} a = "four" print(a) {1: "one", 2: "two", 3: "three", 4: "four"}

В приведенном выше коде применяется оператор присваивания, благодаря чему новая пара (4: “four”) помещается в конец уже созданной ранее коллекции a.

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

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

A = {1: "one", 2: "two", 3: "three"} b = {4: "four", 5: "five"} a.update(b) print(a) {1: "one", 2: "two", 3: "three", 4: "four", 5: "five"}

Результатом работы метода print станет вывод на экран обновленного содержимого словаря под названием a.

После объединения, новые элементы были автоматически записаны в конец коллекции.

Удаление элемента

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

A = {1: "one", 2: "two", 3: "three"} del a print(a) {1: "one", 2: "two"}

Так как операция получила ключ 3, в результате ее работы удалилось и значение three.

Получение размера

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

A = {1: "one", 2: "two", 3: "three"} print(len(a)) 3

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

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

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

Перебор элементов можно осуществить с целью получения для последующей обработки:

  • Пар ключ-значение;
  • Перебор всех ключей;
  • Перебор значений.

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

A = {1: "one", 2: "two", 3: "three"} for key, value in a.items(): print(key, ":", value) 1: one 2: two 3: three

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

A = {1: "one", 2: "two", 3: "three"} for key in a.keys(): print(key) 1 2 3

Аналогичным образом нужно поступить, чтобы вывести только значения словаря . Однако в таком случае в цикле for используется метод values.

A = {1: "one", 2: "two", 3: "three"} for val in a.values(): print(val) one two three

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

Поиск

Проверить наличие определенного ключа можно при помощи операции in . Для этого достаточно вывести результат ее выполнения для словаря по имени a.

A = {1: "one", 2: "two", 3: "three"} print(2 in a) print(4 in a) True False

Как можно заметить, проверка ключа 2 дала положительный результат (True). Во втором случае вывелось значение False, поскольку ключа 4 в словаре не обнаружено.

Сортировка

Средства языка дают возможность проводить в Python сортировку словаря по ключам и значениям, в зависимости от необходимости. В следующем примере имеется коллекция данных по имени a, в которой содержится информация в произвольном порядке. Ключами здесь выступают числа, а значениями являются строки. Сортировка осуществляется за счет импортированного модуля operator и встроенного метода itemgetter , получающего 0 или 1.

Import operator a = {2: "two", 3: "three", 1: "one"} b = sorted(a.items(), key = operator.itemgetter(0)) print(b) b = sorted(a.items(), key = operator.itemgetter(1)) print(b) [(1, "one"), (2, "two"), (3, "three")] [(1, "one"), (3, "three"), (2, "two")]

Как можно заметить, аргумент 0 позволяет отсортировать словарь по ключу, в то время как 1 дает возможность вывести его содержимое в алфавитном порядке значений.

Сравнение

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

A = {1: "one", 2: "two", 3: "three"} b = {4: "four", 5: "five"} c = {1: "one", 2: "two", 3: "three"} print(cmp(a, b)) print(cmp(b, c)) print(cmp(a, c)) 1 -1 0

Приведенный код продемонстрировал выполнение метода cmp с трема комбинациями аргументов. Как видно из результатов выдачи, функция возвращает 1, если первый больше второго, -1, если наоборот и 0, когда данные полностью идентичны.

Копирование

Метод copy используется для копирования содержимого одного словаря в другой . Данный пример демонстрирует перенос ключей и значений из коллекции a в b.

A = {1: "one", 2: "two", 3: "three"} b = a.copy() print(b) {1: "one", 2: "two", 3: "three"}

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

Очистка

Чтобы избавиться от всех элементов словаря, стоит вызвать для него функцию clear .

A = {1: "one", 2: "two", 3: "three"} a.clear() print(a) {}

В результате получается абсолютно пустой набор данных.

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

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

A = {a: a * a for a in range(5)} print(a) {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

Таким образом, на выходе получается словарь a, включающий в себя ровно 5 пар. Ключами являются числа от 0 до 4, а значениями выступают их математические квадраты.

Конвертация в строку

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

A = {1: "one", 2: "two", 3: "three"} b = str(a) print(b) print(type(b)) {1: "one", 2: "two", 3: "three"}

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

A = "{1: "one", 2: "two", 3: "three"}" b = eval(a) print(b) print(type(b)) {1: "one", 2: "two", 3: "three"}

Как видно из примера, метод eval конвертирует весь текст строки в новый словарь.

Вложенные

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

A = { "First": { 1: "one", 2: "two", 3: "three" }, "Second": { 4: "four", 5: "five" } } print(a) {"First": {1: "one", 2: "two", 3: "three"}, "Second": {4: "four", 5: "five"}}

В примере, описанном выше, создается словарь a, включающий в себя два других словаря (First и Second). Те, в свою очередь, содержат несколько пар ключей и значений.

Резюме

Следующая таблица демонстрирует краткую сводку по всем рассмотренным методам для работы со словарями в Python 3 . В таблице отображаются названия методов, а также информация о их назначении.




Top