Slide 1

Slide 1 text

Dictionary в Python По мотивам Objects/dictnotes.txt Cyril @notorca Lashkevich piątek, 30 sierpnia 13

Slide 2

Slide 2 text

Как создать словарь {} dict() PyObject* PyDict_New() piątek, 30 sierpnia 13

Slide 3

Slide 3 text

Сколько словарей в Hello World? $ python -c "print('Hello world')" | wc -l piątek, 30 sierpnia 13

Slide 4

Slide 4 text

Сколько словарей в Hello World? $ python -c "print('Hello world')" | wc -l 1642 piątek, 30 sierpnia 13

Slide 5

Slide 5 text

Именованные параметры функицй 1 запись, 1 чтение 1-3 элемента Часто встречается в обычных программах на Python piątek, 30 sierpnia 13

Slide 6

Slide 6 text

Поиск метода в классе 1 запись, много чтений 8-16 элементов При наследовании много неудачных чтений с последующим поиском в базовом классе piątek, 30 sierpnia 13

Slide 7

Slide 7 text

Атрибуты и глобальные пременные Много записей и чтений 4-10 элементов piątek, 30 sierpnia 13

Slide 8

Slide 8 text

Builtins Частые чтение, почти не бывает записи ~150 строковых ключей (3.3) По некоторым ключам чтения гораздо чаще чем по другим piątek, 30 sierpnia 13

Slide 9

Slide 9 text

Удаление повторов, подсчет элементов Одинократное чтение по каждому из ключей Произвольное количество элементов Многократный доступ по одному ключу подряд piątek, 30 sierpnia 13

Slide 10

Slide 10 text

Удаление дубликатов dict.fromkeys(seqn).keys() Все операции записи при конструировании piątek, 30 sierpnia 13

Slide 11

Slide 11 text

Подсчет элементов в последовательности for e in seqn: d[e] = d.get(e,0) + 1 2 последовательных доступа по одинаковому ключу piątek, 30 sierpnia 13

Slide 12

Slide 12 text

Создание индекса из словаря списков setdefault совмещает 2 поиска в 1м for pnum, page in enumerate(pages): for w in page: d.setdefault(w, []).append(pnum) piątek, 30 sierpnia 13

Slide 13

Slide 13 text

Проверка принадлежности Словари произвольных размеров Создаются 1 раз и затем мало изменяются Много вызовов has_key() и __contains__() piątek, 30 sierpnia 13

Slide 14

Slide 14 text

Динамические отображения Чередующиеся добавления, удаления, чтение и перезапись элементов piątek, 30 sierpnia 13

Slide 15

Slide 15 text

Реализация (2.7) Последовательная область памяти с доступом по индксу typedef struct { Py_hash_t me_hash; PyObject *me_key; PyObject *me_value; } PyDictKeyEntry; piątek, 30 sierpnia 13

Slide 16

Slide 16 text

Пустой dict с размером по умолчанию (8 элементов) >>> d = {} piątek, 30 sierpnia 13

Slide 17

Slide 17 text

Хеширование ключа Ключ преобразуется в индекс с помощъю функции hash() hash() возвращает 32/64bit значение Для индекса берется n младших бит piątek, 30 sierpnia 13

Slide 18

Slide 18 text

Свойства хеша Для равных значений хеши всегда равны Даже если представление значений разное: 9, 9.0, complex(9,0) Похожие значения дают сильно отличающиеся хеши piątek, 30 sierpnia 13

Slide 19

Slide 19 text

>>> d['ftp'] = 21 >>> bits(hash('ftp'))[-8:] 10100001 piątek, 30 sierpnia 13

Slide 20

Slide 20 text

>>> d['ftp'] = 21 >>> bits(hash('ftp'))[-8:] 10100001 piątek, 30 sierpnia 13

Slide 21

Slide 21 text

>>> d['ssh'] = 22 >>> bits(hash('ssh'))[-3:] 101 piątek, 30 sierpnia 13

Slide 22

Slide 22 text

>>> d['ssh'] = 22 >>> bits(hash('ssh'))[-3:] 101 piątek, 30 sierpnia 13

Slide 23

Slide 23 text

>>> d['smtp'] = 25 >>> bits(hash('smtp'))[-3:] 100 piątek, 30 sierpnia 13

Slide 24

Slide 24 text

>>> d['smtp'] = 25 >>> bits(hash('smtp'))[-3:] 100 piątek, 30 sierpnia 13

Slide 25

Slide 25 text

>>> d['time'] = 37 >>> bits(hash('time'))[-3:] 111 piątek, 30 sierpnia 13

Slide 26

Slide 26 text

>>> d['time'] = 37 >>> bits(hash('time'))[-3:] 111 piątek, 30 sierpnia 13

Slide 27

Slide 27 text

>>> d['www'] = 80 >>> bits(hash('www'))[-3:] 010 piątek, 30 sierpnia 13

Slide 28

Slide 28 text

>>> d['www'] = 80 >>> bits(hash('www'))[-3:] 010 piątek, 30 sierpnia 13

Slide 29

Slide 29 text

d = {'ftp': 21, 'ssh': 22, 'smtp': 25, 'time': 37, 'www': 80} piątek, 30 sierpnia 13

Slide 30

Slide 30 text

Поиск в словаре Вычислить хеш от ключа Обрезать старшие биты Взять значение из слота по индексу piątek, 30 sierpnia 13

Slide 31

Slide 31 text

>>> d['smtp'] 25 >>> bits(hash('smtp'))[-3:] 100 piątek, 30 sierpnia 13

Slide 32

Slide 32 text

Перебор всех элементов Словари возвращают свои ключи или значения в порядке отличном от порядка добавления piątek, 30 sierpnia 13

Slide 33

Slide 33 text

>>> print d {'ftp': 21, 'www': 80, 'smtp': 25, 'ssh': 22, 'time': 37} piątek, 30 sierpnia 13

Slide 34

Slide 34 text

>>> d.keys() ['ftp', 'www', 'smtp', 'ssh', 'time'] piątek, 30 sierpnia 13

Slide 35

Slide 35 text

>>> d.values() [21, 80, 25, 22, 37] piątek, 30 sierpnia 13

Slide 36

Slide 36 text

Коллизии Разные ключи пытаются доступиться по одинаковому индексу Находим первое свободное место piątek, 30 sierpnia 13

Slide 37

Slide 37 text

>>> d = {} piątek, 30 sierpnia 13

Slide 38

Slide 38 text

>>> d['smtp'] = 21 piątek, 30 sierpnia 13

Slide 39

Slide 39 text

>>> d['smtp'] = 21 piątek, 30 sierpnia 13

Slide 40

Slide 40 text

>>> d['dict'] = 2628 piątek, 30 sierpnia 13

Slide 41

Slide 41 text

>>> d['dict'] = 2628 piątek, 30 sierpnia 13

Slide 42

Slide 42 text

>>> d['svn'] = 3690 piątek, 30 sierpnia 13

Slide 43

Slide 43 text

>>> d['svn'] = 3690 piątek, 30 sierpnia 13

Slide 44

Slide 44 text

>>> d['ircd'] = 6667 piątek, 30 sierpnia 13

Slide 45

Slide 45 text

>>> d['ircd'] = 6667 piątek, 30 sierpnia 13

Slide 46

Slide 46 text

>>> d['zope'] = 9673 piątek, 30 sierpnia 13

Slide 47

Slide 47 text

>>> d['zope'] = 9673 # 2 из 5ти элементов на своих ожидаемых местах piątek, 30 sierpnia 13

Slide 48

Slide 48 text

Коллизии и очередность Поскольку из за коллизий элементы могут находится не по своим "естественным" индексам порядок элементов зависит от порядка добавления piątek, 30 sierpnia 13

Slide 49

Slide 49 text

Поиск первой свободной ячейки Последовательный поиск плох для int ключей pertrurb = hash while (<слот занят>) { slot = (5*slot) + 1 + perturb; perturb >>= 5; } piątek, 30 sierpnia 13

Slide 50

Slide 50 text

>>> d['svn'] 3690 piątek, 30 sierpnia 13

Slide 51

Slide 51 text

>>> d['ircd'] 6667 piątek, 30 sierpnia 13

Slide 52

Slide 52 text

>>> d['nsca'] KeyError: 'nsca' piątek, 30 sierpnia 13

Slide 53

Slide 53 text

>>> d['netstat'] KeyError: 'netstat' piątek, 30 sierpnia 13

Slide 54

Slide 54 text

Не все поиски одинаковы Некоторые находят результат сразу Некоторым нужны несколько итераций piątek, 30 sierpnia 13

Slide 55

Slide 55 text

threes = {3: 1, 3+8: 2, 3+16: 3, 3+24: 4, 3+32: 5} piątek, 30 sierpnia 13

Slide 56

Slide 56 text

Удаление элементов Нелзя просто так взять, и пометить ячейку как пустую Необходимо вставить специальный "dummy" элемент piątek, 30 sierpnia 13

Slide 57

Slide 57 text

del d['smtp'] piątek, 30 sierpnia 13

Slide 58

Slide 58 text

del d['smtp'] d['ircd'] ??? piątek, 30 sierpnia 13

Slide 59

Slide 59 text

del d['smtp'] #Заменяем на "dummy" слот #Может быть использован снова piątek, 30 sierpnia 13

Slide 60

Slide 60 text

del d['smtp'] #Заменяем на "dummy" слот #Может быть использован снова piątek, 30 sierpnia 13

Slide 61

Slide 61 text

>>> del d['svn'], d['dict'], d['zope'] >>> d['ircd'] piątek, 30 sierpnia 13

Slide 62

Slide 62 text

Увеличение размера таблицы Таблица заполнена максимум на 2/3 2.7: < 50k size × 4 > 50k size × 2 3.3: size × 2 piątek, 30 sierpnia 13

Slide 63

Slide 63 text

>>> d = {} piątek, 30 sierpnia 13

Slide 64

Slide 64 text

d = dict.fromkeys(words[:5]) # 40% коллизий # Заполнен на ⅔, resize piątek, 30 sierpnia 13

Slide 65

Slide 65 text

d['abash'] = None # размер ×4 до 32 # 0% коллизий piątek, 30 sierpnia 13

Slide 66

Slide 66 text

d = dict.fromkeys(words[:21]) # 29% коллизий # Заполнен на ⅔ piątek, 30 sierpnia 13

Slide 67

Slide 67 text

d['abode'] = None # размер ×4 до 128 # 9% коллизий piątek, 30 sierpnia 13

Slide 68

Slide 68 text

d = dict.fromkeys(words[:85]) # 33% коллизий # Заполнен на ⅔ piątek, 30 sierpnia 13

Slide 69

Slide 69 text

Время доступа к элементам Растет по мере заполнения словаря Затем резко умешьшается после изменения размера Среднее время доступа ОК piątek, 30 sierpnia 13

Slide 70

Slide 70 text

Поиски vs размер piątek, 30 sierpnia 13

Slide 71

Slide 71 text

Время vs размер piątek, 30 sierpnia 13

Slide 72

Slide 72 text

Удаление элементов Не уменьшает размер таблицы Таблица может уменьшиться только при добавлении элементов piątek, 30 sierpnia 13

Slide 73

Slide 73 text

Порядок элементов Во время изменения размера порядок элементов может полностью поменяться Добавление элементов во время итерации запрещено RuntimeError: dictionary changed size during iteration piątek, 30 sierpnia 13

Slide 74

Slide 74 text

Свой __hash__() Хорошо перемешать биты Равные хеши для равных элементов __eq__() должен быть Быстро вычисляется piątek, 30 sierpnia 13

Slide 75

Slide 75 text

Пример __hash__() class Point(object): def __init__(self, x, y): self.x, self.y = x, y def __eq__(self, p): return self.x==p.x and self.y==p.y def __hash__(self): return hash(self.x) ^ hash(self.y) piątek, 30 sierpnia 13

Slide 76

Slide 76 text

oCERT #2011-003 Хэш для str, bytes и datetime смешивается с "солью" уникальной для каждого процесса Python pre 3.3: -R option 3.3: by default piątek, 30 sierpnia 13

Slide 77

Slide 77 text

Python 3: Split-table словари. Общая таблица с ключами для разных таблиц со значениями piątek, 30 sierpnia 13

Slide 78

Slide 78 text

Спасибо http://blip.tv/pycon-us- videos-2009-2010-2011/pycon-2010- the-mighty-dictionary-55-3352147 Python source code piątek, 30 sierpnia 13