- •Об авторе
- •О группе редакторов
- •Предисловие
- •Введение
- •Как использовать эту книгу
- •Загрузка исходного кода CPython
- •Что в исходном коде?
- •Настройка среды разработки
- •IDE или редактор?
- •Настройка Visual Studio
- •Настройка Visual Studio Code
- •Настройка Vim
- •Выводы
- •Компиляция CPython
- •Компиляция CPython на macOS
- •Компиляция CPython на Linux
- •Установка специализированной версии
- •Знакомство с Make
- •Make-цели CPython
- •Компиляция CPython на Windows
- •Профильная оптимизация
- •Выводы
- •Грамматика и язык Python
- •Спецификация языка Python
- •Генератор парсеров
- •Повторное генерирование грамматики
- •Выводы
- •Конфигурация и ввод
- •Конфигурация состояния
- •Структура данных конфигурации среды выполнения
- •Конфигурация сборки
- •Сборка модуля из входных данных
- •Выводы
- •Генерирование конкретного синтаксического дерева
- •Парсер/токенизатор CPython
- •Абстрактные синтаксические деревья
- •Важные термины
- •Пример: добавление оператора «почти равно»
- •Выводы
- •Компилятор
- •Исходные файлы
- •Важные термины
- •Создание экземпляра компилятора
- •Флаги будущей функциональности и флаги компилятора
- •Таблицы символических имен
- •Основная компиляция
- •Ассемблер
- •Создание объекта кода
- •Использование Instaviz для вывода объекта кода
- •Пример: реализация оператора «почти равно»
- •Выводы
- •Цикл вычисления
- •Исходные файлы
- •Важные термины
- •Построение состояния потока
- •Построение объектов кадров
- •Выполнение кадра
- •Стек значений
- •Пример: добавление элемента в список
- •Выводы
- •Управление памятью
- •Выделение памяти в C
- •Проектирование системы управления памятью Python
- •Аллокаторы памяти CPython
- •Область выделения объектной памяти и PyMem
- •Область выделения сырой памяти
- •Нестандартные области выделения памяти
- •Санитайзеры выделенной памяти
- •Арена памяти PyArena
- •Подсчет ссылок
- •Сборка мусора
- •Выводы
- •Параллелизм и конкурентность
- •Модели параллелизма и конкурентности
- •Структура процесса
- •Многопроцессорный параллелизм
- •Многопоточность
- •Асинхронное программирование
- •Генераторы
- •Сопрограммы
- •Асинхронные генераторы
- •Субинтерпретаторы
- •Выводы
- •Объекты и типы
- •Примеры этой главы
- •Встроенные типы
- •Типы объектов
- •Тип type
- •Типы bool и long
- •Тип строки Юникода
- •Словари
- •Выводы
- •Стандартная библиотека
- •Модули Python
- •Модули Python и C
- •Набор тестов
- •Запуск набора тестов в Windows
- •Запуск набора тестов в Linux или macOS
- •Флаги тестирования
- •Запуск конкретных тестов
- •Модули тестирования
- •Вспомогательные средства тестирования
- •Выводы
- •Отладка
- •Обработчик сбоев
- •Компиляция поддержки отладки
- •LLDB для macOS
- •Отладчик Visual Studio
- •Отладчик CLion
- •Выводы
- •Бенчмаркинг, профилирование и трассировка
- •Использование timeit для микробенчмарка
- •Использование набора тестов производительности Python
- •Профилирование кода Python с использованием cProfile
- •Выводы
- •Что дальше?
- •Создание расширений C для CPython
- •Улучшение приложений Python
- •Участие в проекте CPython
- •Дальнейшее обучение
- •Препроцессор C
- •Базовый синтаксис C
- •Выводы
- •Благодарности
282 Объекты и типы
СЛОВАРИ
Словари — быстрый и гибкий способ сопоставления. Они используются разработчиками для хранения и сопоставления данных, а также объектами Python для хранения свойств и методов.
Словари Python также используются для локальных и глобальных переменных, для именованных аргументов и многих других сценариев использования. Словари Python компактны, так как в хеш-таблицах хранятся только сопоставляемые значения. Алгоритм хеширования, который является частью всех неизменяемых встроенных типов, работает быстро. Именно он обеспечивает высокую скорость словарей Python.
Хеширование
Все неизменяемые встроенные типы предоставляют функцию хеширования. Эта функция определяется в слоте типа tp_hash или (для пользовательских типов) при помощи специального метода __hash__(). Хеш имеет такой же размер, как указатель (64 бита для 64-разрядных систем, 32 бита для 32-раз- рядных систем), но он не представляет адреса своего значения в памяти.
Результат хеширования, полученный для любого объекта Python, не должен изменяться на протяжении всего жизненного цикла объекта. Хеши двух неизменяемых экземпляров, содержащих одинаковые значения, должны быть равны:
>>> "hello".__hash__() == ("hel" + "lo").__hash__() True
Коллизии хешей недопустимы. Два объекта с разными значениями не должны иметь одинаковые хеши.
Некоторые хеши очень просты, например, у типа Python long:
>>> (401).__hash__() 401
Для более длинных значений хеши усложняются:
>>> (401123124389798989898).__hash__() 2212283795829936375
Многие встроенные типы используют модуль Python pyhash.c, который предоставляет следующие вспомогательные функции хеширования:
Книги для программистов: https://t.me/booksforits
Словари 283
zz Байты: _Py_HashBytes(const void*, Py_ssize_t) zz Double: _Py_HashDouble(double)
zz Указатели: _Py_HashPointer(void*)
Например, строки Юникода используют _Py_HashBytes() для хеширования байтовых данных строки:
>>> ("hello").__hash__() 4894421526362833592
Пользовательские классы могут определять функцию хеширования, реализуя __hash__(). Вместо того чтобы писать специальную реализацию хеш-функции, добавьте в класс уникальное свойство. Позаботьтесь о том, чтобы свойство было неизменяемым, сделав его доступным только для чтения, а затем хешируйте его встроенной функцией hash():
class User:
def __init__(self, id: int, name: str, address: str): self._id = id
def __hash__(self): return hash(self._id)
@property def id(self):
return self._id
Теперь экземпляры этого класса могут хешироваться:
>>>bob = User(123884, "Bob Smith", "Townsville, QLD")
>>>hash(bob)
123884
Экземпляр может использоваться в качестве ключа словаря:
>>>sally = User(123823, "Sally Smith", "Cairns, QLD")
>>>near_reef = {bob: False, sally: True}
>>>near_reef[bob]
False
Множества удаляют дублирующиеся хеши этого экземпляра:
>>> {bob, bob}
{<__main__.User object at 0x10df244b0>}
Книги для программистов: https://t.me/booksforits
284 Объекты и типы
Исходные файлы
Ниже перечислены исходные файлы, относящиеся к словарям.
ФАЙЛ |
НАЗНАЧЕНИЕ |
Include dictobject.h |
Определение API объекта словаря |
Include cpython dictobject.h |
Определение типов объекта словаря |
Objects dictobject.c |
Реализация объекта словаря |
Objects dict-common.h |
Определение элемента словаря и объектов |
|
ключей |
Python pyhash.c |
Внутренний алгоритм хеширования |
Структура словаря
Объект словаря PyDictObject состоит из следующих элементов:
1.Свойства объекта словаря: размер, метка версии, ключи и значения.
2.Объект таблицы ключей словаря PyDictKeysObject, содержащий ключи и хеши всех элементов.
|
|
|
PyDictObject |
|
||||
|
|
|
PyDictKeysObject |
|
||||
|
|
|
||||||
|
|
|
|
|
|
|
|
( ) |
|
|
|
|
|
(PyObject *) |
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(PyObject *) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(PyObject *) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(PyObject*)
(PyObject *)
-
Книги для программистов: https://t.me/booksforits
Словари 285
Объект PyDictObject содержит следующие свойства.
ПОЛЕ |
ТИП |
НАЗНАЧЕНИЕ |
ma_keys |
PyDictKeysObject* |
Объект таблицы ключей словаря |
ma_used |
Py_ssize_t |
Количество элементов в словаре |
ma_values |
PyObject** |
Необязательный массив значений (см. при- |
|
|
мечание) |
ma_version_tag |
uint64_t |
Номер версии словаря |
ПРИМЕЧАНИЕ
Словари могут находиться в одном из двух состояний: разделенном или объединенном.В объединенном словаре указатели на значения словаря хранятся в таблице ключей.
В разделенном словаре значения хранятся в дополнительном свойстве ma_values в виде таблицы значений PyObject*.
Таблица ключей словаря PyDictKeysObject содержит следующие свойства.
ПОЛЕ |
ТИП |
НАЗНАЧЕНИЕ |
dk_entries |
PyDictKeyEntry[] |
Массив записей ключей словаря |
dk_indices |
char[] |
Хеш-таблица и соответствия для dk_entries |
dk_lookup |
dict_lookup_func |
Функция поиска (см. следующий раздел) |
dk_nentries |
Py_ssize_t |
Количество используемых элементов в табли- |
|
|
це элементов |
dk_refcnt |
Py_ssize_t |
Счетчик ссылок |
dk_size |
Py_ssize_t |
Размер хеш-таблицы |
dk_usable |
Py_ssize_t |
Количество пригодных для использования |
|
|
записей в таблице — если 0, размер словаря |
|
|
изменяется |
Книги для программистов: https://t.me/booksforits
286 Объекты и типы
Запись ключа словаря PyDictKeyEntry содержит следующие свойства.
ПОЛЕ |
ТИП |
НАЗНАЧЕНИЕ |
me_hash |
Py_ssize_t |
Кешированный хеш me_key |
me_key |
PyObject* |
Указатель на объект ключа |
me_value |
PyObject* |
Указатель на объект значения (в объединенном |
|
|
состоянии) |
Поиск
Для заданного объекта ключа существует обобщенная функция поиска lookdict().
Поиск в словаре должен обеспечивать обработку трех возможных ситуаций:
1.В таблице ключей существует адрес ключа в памяти.
2.В таблице ключей существует хеш объекта.
3.Ключ не существует в словаре.
СМ. ТАКЖЕ
ФункцияпоискаосновананаматериалахзнаменитойкнигиДональдаКну та«Искусство программирования»(«TheArt of Computer Programming»).
Хеширование рассматривается в 4-м разделе 6-й главы.
Функция поиска работает по следующей схеме:
1.Получить хеш ob.
2.Найти хеш ob в ключах словаря и получить индекс ix.
3.Если значение ix пусто, вернуть DKIX_EMPTY (не найдено).
4.Получить запись ключа ep для заданного индекса.
5.Если значения ключей совпадают, то ob указывает на то же значение ключа. Вернуть результат.
6.Если хеши ключей совпадают, потому что объект ob разрешает то же значение хеша, что и ep->me_mash, вернуть результат.
Книги для программистов: https://t.me/booksforits