Документация Python неофициальный перевод

pickle.md

820 строк · 89.4 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.8/library/pickle.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle) – сериализация объектов Python89**Исходный код:** [Lib/pickle.py](https://python-all.ru/src/3.8/Lib/pickle.py)1011---1213Модуль [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle) реализует двоичные протоколы для сериализации и de-сериализации структуры объектов Python. *«Упаковка»* – это процесс преобразования иерархии объектов Python в поток байтов, а *«распаковка»* – обратная операция, при которой поток байтов (из [двоичного файла](https://python-all.ru/3.8/glossary.html#term-binary-file) или [байтоподобного объекта](https://python-all.ru/3.8/glossary.html#term-bytes-like-object)) преобразуется обратно в иерархию объектов. Упаковка (и распаковка) также известна как «сериализация», «маршалинг», [1](https://python-all.ru/3.8/library/pickle.html#id7) или «уплощение»; однако, чтобы избежать путаницы, здесь используются термины «упаковка» и «распаковка».1415> **Предупреждение**16>17> Модуль `pickle` **небезопасен**. Распаковывайте только те данные, которым доверяете.18>19> Существует возможность создать вредоносные данные pickle, которые **выполнят произвольный код во время распаковки**. Никогда не распаковывайте данные, которые могли поступить из ненадёжного источника или могли быть изменены.20>21> Если необходимо гарантировать, что данные не были изменены, рассмотрите их подписание с помощью [`hmac`](https://python-all.ru/3.8/library/hmac.html#module-hmac).22>23> Более безопасные форматы сериализации, такие как [`json`](https://python-all.ru/3.8/library/json.html#module-json), могут быть более подходящими, если вы обрабатываете ненадёжные данные. См. [Сравнение с json](https://python-all.ru/3.8/library/pickle.html#comparison-with-json).2425## Отношение к другим модулям Python2627### Сравнение с `marshal`2829В Python есть более примитивный модуль сериализации [`marshal`](https://python-all.ru/3.8/library/marshal.html#module-marshal), но в целом [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle) всегда должен быть предпочтительным способом сериализации объектов Python. [`marshal`](https://python-all.ru/3.8/library/marshal.html#module-marshal) существует в первую очередь для поддержки файлов `.pyc` в Python.3031Модуль [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle) отличается от [`marshal`](https://python-all.ru/3.8/library/marshal.html#module-marshal) несколькими важными особенностями:3233- Модуль [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle) отслеживает уже сериализованные объекты, чтобы последующие ссылки на тот же объект не сериализовались повторно. [`marshal`](https://python-all.ru/3.8/library/marshal.html#module-marshal) этого не делает.3435  This has implications both for recursive objects and object sharing. Recursive objects are objects that contain references to themselves. These are not handled by marshal, and in fact, attempting to marshal recursive objects will crash your Python interpreter. Object sharing happens when there are multiple references to the same object in different places in the object hierarchy being serialized. [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle) stores such objects only once, and ensures that all other references point to the master copy. Shared objects remain shared, which can be very important for mutable objects.36- [`marshal`](https://python-all.ru/3.8/library/marshal.html#module-marshal) нельзя использовать для сериализации пользовательских классов и их экземпляров. [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle) может прозрачно сохранять и восстанавливать экземпляры классов, однако определение класса должно быть импортируемо и находиться в том же модуле, что и при сохранении объекта.37- Формат сериализации [`marshal`](https://python-all.ru/3.8/library/marshal.html#module-marshal) не гарантирует переносимость между версиями Python. Поскольку его основная задача – поддержка файлов `.pyc`, разработчики Python оставляют за собой право изменять формат сериализации несовместимым с предыдущими версиями образом, если возникнет необходимость. Формат сериализации [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle) гарантированно обратно совместим между выпусками Python при условии выбора совместимого протокола pickle и обработки различий типов между Python 2 и Python 3 в коде упаковки и распаковки, если ваши данные пересекают эту уникальную границу изменения языка.3839### Сравнение с `json`4041Существуют принципиальные различия между протоколами pickle и [JSON (JavaScript Object Notation)](https://python-all.ru/3.8/library/pickle.html):4243- JSON – это текстовый формат сериализации (он выводит текст в юникоде, хотя чаще всего затем кодируется в `utf-8`), тогда как pickle – двоичный формат сериализации;44- JSON удобочитаем, а pickle – нет;45- JSON интероперабелен и широко используется за пределами экосистемы Python, тогда как pickle является специфичным для Python;46- JSON по умолчанию может представлять только подмножество встроенных типов Python и никаких пользовательских классов; pickle может представлять огромное количество типов Python (многие из них автоматически, благодаря умелому использованию возможностей интроспекции Python; сложные случаи можно решить, реализовав [специфические объектные API](https://python-all.ru/3.8/library/pickle.html#pickle-inst));47- В отличие от pickle, десериализация ненадёжного JSON сама по себе не создаёт уязвимости для выполнения произвольного кода.4849> **См. также**50>51> Модуль [`json`](https://python-all.ru/3.8/library/json.html#module-json): модуль стандартной библиотеки, обеспечивающий сериализацию и десериализацию JSON.5253## Формат потока данных5455Формат данных, используемый [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle), является специфичным для Python. Это даёт преимущество – нет ограничений, накладываемых внешними стандартами вроде JSON или XDR (которые не могут представлять разделяемые указатели); однако это означает, что программы на других языках могут не суметь восстановить объекты Python, сериализованные с помощью pickle.5657По умолчанию формат данных [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle) использует относительно компактное двоичное представление. Если вам нужны оптимальные характеристики размера, вы можете эффективно [сжимать](https://python-all.ru/3.8/library/archiving.html) упакованные данные.5859Модуль [`pickletools`](https://python-all.ru/3.8/library/pickletools.html#module-pickletools) содержит инструменты для анализа потоков данных, созданных [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle). Исходный код [`pickletools`](https://python-all.ru/3.8/library/pickletools.html#module-pickletools) содержит обширные комментарии об опкодах, используемых протоколами pickle.6061В настоящее время существует 6 различных протоколов, которые можно использовать для упаковки. Чем выше используемый протокол, тем более новая версия Python требуется для чтения полученного pickle.6263- Протокол версии 0 – это исходный «человекочитаемый» протокол, обратно совместимый с более ранними версиями Python.64- Протокол версии 1 – это старый двоичный формат, также совместимый с более ранними версиями Python.65- Протокол версии 2 был представлен в Python 2.3. Он обеспечивает гораздо более эффективную сериализацию [классов нового стиля](https://python-all.ru/3.8/glossary.html#term-new-style-class). Дополнительную информацию об улучшениях, внесённых протоколом 2, см. в [**PEP 307**](https://python-all.ru/3.8/library/pickle.html).66- Протокол версии 3 был добавлен в Python 3.0. Он имеет явную поддержку объектов [`bytes`](https://python-all.ru/3.8/library/stdtypes.html#bytes) и не может быть распакован в Python 2.x. Это был протокол по умолчанию в Python 3.0–3.7.67- Версия протокола 4 была добавлена в Python 3.4. Она добавляет поддержку очень больших объектов, сериализацию большего количества типов объектов и некоторые оптимизации формата данных. Начиная с Python 3.8 это протокол по умолчанию. Смотрите [**PEP 3154**](https://python-all.ru/3.8/library/pickle.html) для информации об улучшениях, внесённых протоколом 4.68- Версия протокола 5 была добавлена в Python 3.8. Она добавляет поддержку внешних данных и ускорение для внутренних данных. Смотрите [**PEP 574**](https://python-all.ru/3.8/library/pickle.html) для информации об улучшениях, внесённых протоколом 5.6970> **Примечание**71>72> Сериализация – это более примитивное понятие, чем сохранение (persistence); хотя [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle) читает и записывает файловые объекты, он не решает ни проблему именования сохраняемых объектов, ни (ещё более сложную) проблему параллельного доступа к сохраняемым объектам. Модуль [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle) может преобразовать сложный объект в поток байтов и обратно – в объект с той же внутренней структурой. Возможно, самое очевидное применение этих потоков байтов – запись в файл, но также можно передавать их по сети или хранить в базе данных. Модуль [`shelve`](https://python-all.ru/3.8/library/shelve.html#module-shelve) предоставляет простой интерфейс для упаковки (pickle) и распаковки (unpickle) объектов в файлах баз данных в стиле DBM.7374## Интерфейс модуля7576Чтобы сериализовать иерархию объектов, достаточно вызвать функцию [`dumps()`](https://python-all.ru/3.8/library/pickle.html#pickle.dumps). Аналогично, для десериализации потока данных вызывается функция [`loads()`](https://python-all.ru/3.8/library/pickle.html#pickle.loads). Однако если требуется более тонкий контроль над сериализацией и десериализацией, можно создать объект [`Pickler`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler) или [`Unpickler`](https://python-all.ru/3.8/library/pickle.html#pickle.Unpickler) соответственно.7778Модуль [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle) предоставляет следующие константы:7980#### `pickle.HIGHEST_PROTOCOL`8182Целое число – наивысшая доступная [версия протокола](https://python-all.ru/3.8/library/pickle.html#pickle-protocols). Это значение можно передавать в качестве аргумента *протокола* функциям [`dump()`](https://python-all.ru/3.8/library/pickle.html#pickle.dump) и [`dumps()`](https://python-all.ru/3.8/library/pickle.html#pickle.dumps), а также конструктору [`Pickler`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler).8384#### `pickle.DEFAULT_PROTOCOL`8586Целое число, [версия протокола](https://python-all.ru/3.8/library/pickle.html#pickle-protocols) по умолчанию, используемая для сериализации. Может быть меньше [`HIGHEST_PROTOCOL`](https://python-all.ru/3.8/library/pickle.html#pickle.HIGHEST_PROTOCOL). В настоящее время протокол по умолчанию – 4, впервые представленный в Python 3.4 и несовместимый с предыдущими версиями.8788Изменено в версии 3.0: Протокол по умолчанию – 3.8990Изменено в версии 3.8: Протокол по умолчанию – 4.9192Модуль [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle) предоставляет следующие функции для упрощения процесса упаковки:9394#### `pickle.dump(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None)`9596Записывает упакованное представление объекта *obj* в открытый [файловый объект](https://python-all.ru/3.8/glossary.html#term-file-object) *file*. Это эквивалентно `Pickler(file, protocol).dump(obj)`.9798Аргументы *file*, *protocol*, *fix\_imports* и *buffer\_callback* имеют тот же смысл, что и в конструкторе [`Pickler`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler).99100Изменено в версии 3.8: Добавлен аргумент *buffer\_callback*.101102#### `pickle.dumps(obj, protocol=None, *, fix_imports=True, buffer_callback=None)`103104Возвращает упакованное представление объекта *obj* в виде объекта [`bytes`](https://python-all.ru/3.8/library/stdtypes.html#bytes), а не записывает его в файл.105106Аргументы *protocol*, *fix\_imports* и *buffer\_callback* имеют тот же смысл, что и в конструкторе [`Pickler`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler).107108Изменено в версии 3.8: Добавлен аргумент *buffer\_callback*.109110#### `pickle.load(file, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)`111112Читает упакованное представление объекта из открытого [файлового объекта](https://python-all.ru/3.8/glossary.html#term-file-object) *file* и возвращает восстановленную иерархию объектов, заданную в нём. Это эквивалентно `Unpickler(file).load()`.113114Версия протокола pickle определяется автоматически, поэтому аргумент протокол не требуется. Байты после упакованного представления объекта игнорируются.115116Аргументы *file*, *fix\_imports*, *encoding*, *errors*, *strict* и *buffers* имеют тот же смысл, что и в конструкторе [`Unpickler`](https://python-all.ru/3.8/library/pickle.html#pickle.Unpickler).117118Изменено в версии 3.8: Добавлен аргумент *buffers*.119120#### `pickle.loads(data, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)`121122Возвращает восстановленную иерархию объектов упакованного представления *data* объекта. *data* должен быть [байтоподобным объектом](https://python-all.ru/3.8/glossary.html#term-bytes-like-object).123124Версия протокола pickle определяется автоматически, поэтому аргумент протокол не требуется. Байты после упакованного представления объекта игнорируются.125126Аргументы *file*, *fix\_imports*, *encoding*, *errors*, *strict* и *buffers* имеют тот же смысл, что и в конструкторе [`Unpickler`](https://python-all.ru/3.8/library/pickle.html#pickle.Unpickler).127128Изменено в версии 3.8: Добавлен аргумент *buffers*.129130Модуль [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle) определяет три исключения:131132#### `exception pickle.PickleError`133134Общий базовый класс для других исключений pickle. Наследует [`Exception`](https://python-all.ru/3.8/library/exceptions.html#Exception).135136#### `exception pickle.PicklingError`137138Ошибка, возникающая, когда [`Pickler`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler) встречает неупаковываемый объект. Наследует [`PickleError`](https://python-all.ru/3.8/library/pickle.html#pickle.PickleError).139140Обратитесь к разделу [Какие объекты можно упаковывать и распаковывать?](https://python-all.ru/3.8/library/pickle.html#pickle-picklable), чтобы узнать, какие объекты можно упаковывать.141142#### `exception pickle.UnpicklingError`143144Ошибка, возникающая при проблемах распаковки объекта, например, из-за повреждения данных или нарушения безопасности. Наследует [`PickleError`](https://python-all.ru/3.8/library/pickle.html#pickle.PickleError).145146Обратите внимание, что во время распаковки могут возбуждаться и другие исключения, включая (но не ограничиваясь) AttributeError, EOFError, ImportError и IndexError.147148Модуль [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle) экспортирует три класса: [`Pickler`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler), [`Unpickler`](https://python-all.ru/3.8/library/pickle.html#pickle.Unpickler) и [`PickleBuffer`](https://python-all.ru/3.8/library/pickle.html#pickle.PickleBuffer):149150#### `class pickle.Pickler(file, protocol=None, *, fix_imports=True, buffer_callback=None)`151152Он принимает двоичный файл для записи потока данных pickle.153154Необязательный аргумент *протокол* – целое число, указывает упаковщику использовать заданный протокол; поддерживаются протоколы от 0 до [`HIGHEST_PROTOCOL`](https://python-all.ru/3.8/library/pickle.html#pickle.HIGHEST_PROTOCOL). Если аргумент не указан, по умолчанию используется [`DEFAULT_PROTOCOL`](https://python-all.ru/3.8/library/pickle.html#pickle.DEFAULT_PROTOCOL). Если указано отрицательное число, выбирается [`HIGHEST_PROTOCOL`](https://python-all.ru/3.8/library/pickle.html#pickle.HIGHEST_PROTOCOL).155156Аргумент *file* должен иметь метод write(), принимающий единственный аргумент типа bytes. Таким образом, это может быть файл на диске, открытый для двоичной записи, экземпляр [`io.BytesIO`](https://python-all.ru/3.8/library/io.html#io.BytesIO) или любой другой пользовательский объект, соответствующий этому интерфейсу.157158Если *fix\_imports* равен true и *протокол* меньше 3, pickle попытается сопоставить новые имена Python 3 со старыми именами модулей, используемыми в Python 2, чтобы поток данных pickle был читаем в Python 2.159160Если *buffer\_callback* равен None (по умолчанию), то буферные представления сериализуются в *file* как часть потока pickle.161162Если *buffer\_callback* не равен None, то он может быть вызван любое количество раз с буферным представлением. Если колбэк возвращает ложное значение (например None), переданный буфер считается [внеполосным](https://python-all.ru/3.8/library/pickle.html#pickle-oob); в противном случае буфер сериализуется внутриполосно, т.е. внутри потока pickle.163164Ошибка, если *buffer\_callback* не равен None, а *protocol* равен None или меньше 5.165166Изменено в версии 3.8: Добавлен аргумент *buffer\_callback*.167168#### `dump(obj)`169170Записывает упакованное представление *obj* в открытый файловый объект, переданный в конструкторе.171172#### `persistent_id(obj)`173174По умолчанию ничего не делает. Этот метод существует, чтобы подкласс мог его переопределить.175176Если [`persistent_id()`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler.persistent_id) возвращает `None`, *obj* упаковывается как обычно. Любое другое значение заставляет [`Pickler`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler) выдать возвращённое значение в качестве постоянного идентификатора для *obj*. Значение этого постоянного идентификатора должно быть определено [`Unpickler.persistent_load()`](https://python-all.ru/3.8/library/pickle.html#pickle.Unpickler.persistent_load). Обратите внимание, что значение, возвращаемое [`persistent_id()`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler.persistent_id), само не может иметь постоянного идентификатора.177178См. [Постоянство внешних объектов](https://python-all.ru/3.8/library/pickle.html#pickle-persistent) для подробностей и примеров использования.179180#### `dispatch_table`181182Таблица диспетчеризации (dispatch table) объекта Pickler – это реестр *функций приведения (reduction functions)*, которые можно объявить с помощью [`copyreg.pickle()`](https://python-all.ru/3.8/library/copyreg.html#copyreg.pickle). Это отображение, где ключами являются классы, а значениями – функции приведения. Функция приведения принимает единственный аргумент соответствующего класса и должна соответствовать тому же интерфейсу, что и метод [`__reduce__()`](https://python-all.ru/3.8/library/pickle.html#object.__reduce__).183184По умолчанию объект Pickler не имеет атрибута [`dispatch_table`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler.dispatch_table), а вместо этого использует глобальную таблицу диспетчеризации, управляемую модулем [`copyreg`](https://python-all.ru/3.8/library/copyreg.html#module-copyreg). Однако для настройки упаковки для конкретного объекта Pickler можно задать атрибут [`dispatch_table`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler.dispatch_table) как объект, похожий на словарь. Кроме того, если подкласс [`Pickler`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler) имеет атрибут [`dispatch_table`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler.dispatch_table), то он будет использоваться в качестве таблицы диспетчеризации по умолчанию для экземпляров этого класса.185186См. [Таблицы диспетчеризации](https://python-all.ru/3.8/library/pickle.html#pickle-dispatch) в качестве примеров использования.187188Новое в версии 3.3.189190#### `reducer_override(self, obj)`191192Специальная функция приведения, которая может быть определена в подклассах [`Pickler`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler). Этот метод имеет приоритет над любой функцией приведения в [`dispatch_table`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler.dispatch_table). Он должен соответствовать тому же интерфейсу, что и метод [`__reduce__()`](https://python-all.ru/3.8/library/pickle.html#object.__reduce__), и может опционально возвращать `NotImplemented` для перехода к функциям приведения, зарегистрированным в [`dispatch_table`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler.dispatch_table), чтобы упаковать `obj`.193194Подробный пример см. в [Пользовательское приведение для типов, функций и других объектов](https://python-all.ru/3.8/library/pickle.html#reducer-override).195196Новое в версии 3.8.197198#### `fast`199200Устарело. Включает быстрый режим, если установлено в истинное значение. Быстрый режим отключает использование мемо, ускоряя процесс упаковки за счёт отсутствия лишних op-кодов PUT. Не следует использовать с самореферентными объектами, иначе [`Pickler`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler) будет рекурсивно вызывать себя до бесконечности.201202Используйте [`pickletools.optimize()`](https://python-all.ru/3.8/library/pickletools.html#pickletools.optimize), если нужны более компактные упаковки.203204#### `class pickle.Unpickler(file, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)`205206Принимает бинарный файл для чтения потока данных pickle.207208Версия протокола pickle определяется автоматически, поэтому аргумент протокола не нужен.209210Аргумент *file* должен иметь три метода: read(), который принимает целочисленный аргумент, readinto(), принимающий аргумент-буфер, и readline(), не требующий аргументов, как в интерфейсе [`io.BufferedIOBase`](https://python-all.ru/3.8/library/io.html#io.BufferedIOBase). Таким образом, *file* может быть файлом на диске, открытым для бинарного чтения, объектом [`io.BytesIO`](https://python-all.ru/3.8/library/io.html#io.BytesIO) или любым другим пользовательским объектом, соответствующим этому интерфейсу.211212Необязательные аргументы *fix\_imports*, *encoding* и *errors* используются для управления совместимостью с потоком pickle, созданным Python 2. Если *fix\_imports* истинно, pickle попытается сопоставить старые имена Python 2 с новыми именами, используемыми в Python 3. Аргументы *encoding* и *errors* сообщают pickle, как декодировать 8-битные строковые экземпляры, упакованные Python 2; по умолчанию они равны 'ASCII' и 'strict' соответственно. *encoding* может быть 'bytes' для чтения этих 8-битных строк как байтовых объектов. Использование `encoding='latin1'` требуется для распаковки массивов NumPy и экземпляров [`datetime`](https://python-all.ru/3.8/library/datetime.html#datetime.datetime), [`date`](https://python-all.ru/3.8/library/datetime.html#datetime.date) и [`time`](https://python-all.ru/3.8/library/datetime.html#datetime.time), упакованных Python 2.213214Если *buffers* равен None (по умолчанию), то все необходимые для десериализации данные должны содержаться в потоке pickle. Это означает, что аргумент *buffer\_callback* был равен None, когда экземпляр [`Pickler`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler) создавался (или когда вызывались [`dump()`](https://python-all.ru/3.8/library/pickle.html#pickle.dump) или [`dumps()`](https://python-all.ru/3.8/library/pickle.html#pickle.dumps)).215216Если *buffers* не равен None, это должен быть итерируемый объект из объектов с поддержкой буферов, который расходуется каждый раз, когда поток pickle ссылается на [внеполосное](https://python-all.ru/3.8/library/pickle.html#pickle-oob) буферное представление. Такие буферы были переданы по порядку в *buffer\_callback* объекта Pickler.217218Изменено в версии 3.8: Добавлен аргумент *buffers*.219220#### `load()`221222Читает упакованное представление объекта из открытого файлового объекта, переданного конструктору, и возвращает восстановленную иерархию объектов, заданную в нём. Байты после упакованного представления объекта игнорируются.223224#### `persistent_load(pid)`225226По умолчанию вызывает [`UnpicklingError`](https://python-all.ru/3.8/library/pickle.html#pickle.UnpicklingError).227228Если определено, [`persistent_load()`](https://python-all.ru/3.8/library/pickle.html#pickle.Unpickler.persistent_load) должен возвращать объект, указанный постоянным идентификатором *pid*. Если встречен недопустимый постоянный идентификатор, должно быть вызвано [`UnpicklingError`](https://python-all.ru/3.8/library/pickle.html#pickle.UnpicklingError).229230См. [Постоянство внешних объектов](https://python-all.ru/3.8/library/pickle.html#pickle-persistent) для подробностей и примеров использования.231232#### `find_class(module, name)`233234Импортирует *module* при необходимости и возвращает объект с именем *name* из него, где аргументы *module* и *name* являются объектами [`str`](https://python-all.ru/3.8/library/stdtypes.html#str). Обратите внимание: вопреки названию, [`find_class()`](https://python-all.ru/3.8/library/pickle.html#pickle.Unpickler.find_class) также используется для поиска функций.235236Подклассы могут переопределить этот метод для получения контроля над тем, объекты какого типа и каким образом могут быть загружены, что потенциально снижает риски безопасности. См. [Ограничение глобальных объектов](https://python-all.ru/3.8/library/pickle.html#pickle-restrict) для подробностей.237238Возбуждает [событие аудита](https://python-all.ru/3.8/library/sys.html#auditing) `pickle.find_class` с аргументами `module`, `name`.239240#### `class pickle.PickleBuffer(buffer)`241242Обёртка для буфера, представляющего данные, которые можно упаковать. *buffer* должен быть объектом, [предоставляющим буфер](https://python-all.ru/3.8/c-api/buffer.html#bufferobjects), например [байтово-подобным объектом](https://python-all.ru/3.8/glossary.html#term-bytes-like-object) или N-мерным массивом.243244[`PickleBuffer`](https://python-all.ru/3.8/library/pickle.html#pickle.PickleBuffer) сам является поставщиком буфера, поэтому его можно передавать другим API, ожидающим объект, предоставляющий буфер, например [`memoryview`](https://python-all.ru/3.8/library/stdtypes.html#memoryview).245246Объекты [`PickleBuffer`](https://python-all.ru/3.8/library/pickle.html#pickle.PickleBuffer) могут быть сериализованы только с использованием протокола pickle 5 или выше. Они подходят для [внеполосной сериализации](https://python-all.ru/3.8/library/pickle.html#pickle-oob).247248Новое в версии 3.8.249250#### `raw()`251252Возвращает [`memoryview`](https://python-all.ru/3.8/library/stdtypes.html#memoryview) области памяти, лежащей в основе этого буфера. Возвращаемый объект представляет собой одномерный, C-непрерывный memoryview с форматом `B` (беззнаковые байты). [`BufferError`](https://python-all.ru/3.8/library/exceptions.html#BufferError) возникает, если буфер не является ни C-, ни Fortran-непрерывным.253254#### `release()`255256Освобождает базовый буфер, предоставленный объектом PickleBuffer.257258## Какие объекты можно сериализовать и десериализовать?259260Следующие типы можно сериализовать:261262- `None`, `True` и `False`263- целые числа, числа с плавающей запятой, комплексные числа264- строки, байты, байтовые массивы265- кортежи, списки, множества и словари, содержащие только сериализуемые объекты266- функции, определённые на верхнем уровне модуля (с помощью [`def`](https://python-all.ru/3.8/reference/compound_stmts.html#def), а не [`lambda`](https://python-all.ru/3.8/reference/expressions.html#lambda))267- встроенные функции, определённые на верхнем уровне модуля268- классы, определённые на верхнем уровне модуля269- экземпляры таких классов, чей [`__dict__`](https://python-all.ru/3.8/library/stdtypes.html#object.__dict__) или результат вызова [`__getstate__()`](https://python-all.ru/3.8/library/pickle.html#object.__getstate__) можно упаковать (см. раздел [Упаковка экземпляров классов](https://python-all.ru/3.8/library/pickle.html#pickle-inst) для подробностей).270271Попытки сериализовать несериализуемые объекты приведут к возникновению исключения [`PicklingError`](https://python-all.ru/3.8/library/pickle.html#pickle.PicklingError); при этом на диск может быть уже записано неизвестное количество байтов. Попытка сериализовать сильно рекурсивную структуру данных может превысить максимальную глубину рекурсии – в этом случае будет возбуждено [`RecursionError`](https://python-all.ru/3.8/library/exceptions.html#RecursionError). Можно аккуратно увеличить этот лимит с помощью [`sys.setrecursionlimit()`](https://python-all.ru/3.8/library/sys.html#sys.setrecursionlimit).272273Обратите внимание, что функции (встроенные и определённые пользователем) сериализуются по «полному» имени, а не по значению. [2](https://python-all.ru/3.8/library/pickle.html#id8) Это означает, что сериализуется только имя функции вместе с именем модуля, в котором она определена. Ни код функции, ни её атрибуты не сериализуются. Таким образом, определяющий модуль должен быть импортируем в среде десериализации, и модуль должен содержать именованный объект, иначе будет возбуждено исключение. [3](https://python-all.ru/3.8/library/pickle.html#id9)274275Аналогично, классы сериализуются по именованной ссылке, поэтому в среде десериализации действуют те же ограничения. Обратите внимание, что ни код класса, ни его данные не сериализуются, поэтому в следующем примере атрибут класса `attr` не восстанавливается в среде десериализации:276277```python278class Foo:279    attr = 'A class attribute'280281picklestring = pickle.dumps(Foo)282```283284Эти ограничения объясняют, почему сериализуемые функции и классы должны быть определены на верхнем уровне модуля.285286Аналогично, при сериализации экземпляров классов код и данные самого класса не сериализуются вместе с ними. Сериализуются только данные экземпляра. Это сделано намеренно, чтобы можно было исправить ошибки в классе или добавить в него методы и по-прежнему загружать объекты, созданные с помощью более старой версии класса. Если планируется использовать долгоживущие объекты, которые переживут множество версий класса, будет разумно добавить в объекты номер версии, чтобы можно было выполнять соответствующие преобразования с помощью метода [`__setstate__()`](https://python-all.ru/3.8/library/pickle.html#object.__setstate__) класса.287288## Сериализация экземпляров классов289290В этом разделе описываются общие механизмы, позволяющие определять, настраивать и контролировать процесс сериализации и десериализации экземпляров классов.291292В большинстве случаев для того, чтобы экземпляры стали сериализуемыми, не требуется дополнительного кода. По умолчанию pickle получает класс и атрибуты экземпляра через интроспекцию. При десериализации экземпляра класса его метод [`__init__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__init__) обычно *не* вызывается. Поведение по умолчанию сначала создаёт неинициализированный экземпляр, а затем восстанавливает сохранённые атрибуты. Следующий код демонстрирует реализацию такого поведения:293294```python295def save(obj):296    return (obj.__class__, obj.__dict__)297298def load(cls, attributes):299    obj = cls.__new__(cls)300    obj.__dict__.update(attributes)301    return obj302```303304Классы могут изменить поведение по умолчанию, предоставив один или несколько специальных методов:305306#### `object.__getnewargs_ex__()`307308В протоколах 2 и выше классы, реализующие метод [`__getnewargs_ex__()`](https://python-all.ru/3.8/library/pickle.html#object.__getnewargs_ex__), могут задавать значения, передаваемые методу [`__new__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__new__) при распаковке. Метод должен возвращать пару `(args, kwargs)`, где *args* – кортеж позиционных аргументов, а *kwargs* – словарь именованных аргументов для конструирования объекта. Эти значения будут переданы методу [`__new__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__new__) при распаковке.309310Этот метод следует реализовать, если метод [`__new__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__new__) вашего класса требует только именованных аргументов (keyword-only). В противном случае для обеспечения совместимости рекомендуется реализовать [`__getnewargs__()`](https://python-all.ru/3.8/library/pickle.html#object.__getnewargs__).311312Изменено в версии 3.6: [`__getnewargs_ex__()`](https://python-all.ru/3.8/library/pickle.html#object.__getnewargs_ex__) теперь используется в протоколах 2 и 3.313314#### `object.__getnewargs__()`315316Этот метод служит той же цели, что и [`__getnewargs_ex__()`](https://python-all.ru/3.8/library/pickle.html#object.__getnewargs_ex__), но поддерживает только позиционные аргументы. Он должен возвращать кортеж аргументов `args`, который будет передан методу [`__new__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__new__) при десериализации.317318[`__getnewargs__()`](https://python-all.ru/3.8/library/pickle.html#object.__getnewargs__) не будет вызываться, если определён [`__getnewargs_ex__()`](https://python-all.ru/3.8/library/pickle.html#object.__getnewargs_ex__).319320Изменено в версии 3.6: До Python 3.6 в протоколах 2 и 3 вместо [`__getnewargs_ex__()`](https://python-all.ru/3.8/library/pickle.html#object.__getnewargs_ex__) вызывался [`__getnewargs__()`](https://python-all.ru/3.8/library/pickle.html#object.__getnewargs__).321322#### `object.__getstate__()`323324Классы могут дополнительно влиять на то, как упаковываются их экземпляры: если класс определяет метод [`__getstate__()`](https://python-all.ru/3.8/library/pickle.html#object.__getstate__), он вызывается, и возвращаемый объект упаковывается как содержимое экземпляра вместо содержимого его словаря. Если метод [`__getstate__()`](https://python-all.ru/3.8/library/pickle.html#object.__getstate__) отсутствует, [`__dict__`](https://python-all.ru/3.8/library/stdtypes.html#object.__dict__) экземпляра упаковывается как обычно.325326#### `object.__setstate__(state)`327328При распаковке, если класс определяет [`__setstate__()`](https://python-all.ru/3.8/library/pickle.html#object.__setstate__), он вызывается с распакованным состоянием. В этом случае нет требования, чтобы объект состояния был словарём. В противном случае упакованное состояние должно быть словарём, и его элементы присваиваются словарю нового экземпляра.329330> **Примечание**331>332> Если [`__getstate__()`](https://python-all.ru/3.8/library/pickle.html#object.__getstate__) возвращает ложное значение, метод [`__setstate__()`](https://python-all.ru/3.8/library/pickle.html#object.__setstate__) не будет вызван при распаковке.333334Обратитесь к разделу [Обработка объектов с состоянием](https://python-all.ru/3.8/library/pickle.html#pickle-state) за дополнительной информацией о том, как использовать методы [`__getstate__()`](https://python-all.ru/3.8/library/pickle.html#object.__getstate__) и [`__setstate__()`](https://python-all.ru/3.8/library/pickle.html#object.__setstate__).335336> **Примечание**337>338> Во время распаковки для экземпляра могут вызываться некоторые методы, такие как [`__getattr__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__getattr__), [`__getattribute__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__getattribute__) или [`__setattr__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__setattr__). Если эти методы полагаются на выполнение некоторого внутреннего инварианта, тип должен реализовать [`__new__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__new__) для установления такого инварианта, поскольку [`__init__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__init__) не вызывается при распаковке экземпляра.339340Как мы увидим, pickle не использует напрямую описанные выше методы. На самом деле эти методы являются частью протокола копирования, который реализует специальный метод [`__reduce__()`](https://python-all.ru/3.8/library/pickle.html#object.__reduce__). Протокол копирования предоставляет единый интерфейс для получения данных, необходимых для упаковки и копирования объектов. [4](https://python-all.ru/3.8/library/pickle.html#id10)341342Хотя реализация [`__reduce__()`](https://python-all.ru/3.8/library/pickle.html#object.__reduce__) непосредственно в классах является мощной, она чревата ошибками. По этой причине разработчикам классов следует использовать высокоуровневый интерфейс (т.е. [`__getnewargs_ex__()`](https://python-all.ru/3.8/library/pickle.html#object.__getnewargs_ex__), [`__getstate__()`](https://python-all.ru/3.8/library/pickle.html#object.__getstate__) и [`__setstate__()`](https://python-all.ru/3.8/library/pickle.html#object.__setstate__)), когда это возможно. Однако мы покажем случаи, когда использование [`__reduce__()`](https://python-all.ru/3.8/library/pickle.html#object.__reduce__) является единственным вариантом или приводит к более эффективной упаковке, или и то и другое.343344#### `object.__reduce__()`345346В настоящее время интерфейс определён следующим образом. Метод [`__reduce__()`](https://python-all.ru/3.8/library/pickle.html#object.__reduce__) не принимает аргументов и должен возвращать либо строку, либо, желательно, кортеж (возвращаемый объект часто называют «reduce value»).347348Если возвращается строка, она должна интерпретироваться как имя глобальной переменной. Это должно быть локальное имя объекта относительно его модуля; модуль pickle ищет объект в пространстве имён модуля. Такое поведение обычно полезно для одиночек (singletons).349350Если возвращается кортеж, он должен содержать от двух до шести элементов. Необязательные элементы можно опустить или указать в качестве их значения `None`. Семантика каждого элемента следующая (по порядку):351352- Вызываемый объект, который будет вызван для создания начальной версии объекта.353- Кортеж аргументов для вызываемого объекта. Если вызываемый объект не принимает ни одного аргумента, должен быть передан пустой кортеж.354- Необязательно: состояние объекта, которое будет передано методу [`__setstate__()`](https://python-all.ru/3.8/library/pickle.html#object.__setstate__) объекта, как описано ранее. Если у объекта нет такого метода, то значение должно быть словарём, и оно будет добавлено в атрибут [`__dict__`](https://python-all.ru/3.8/library/stdtypes.html#object.__dict__) объекта.355- Необязательно, итератор (а не последовательность), выдающий последовательные элементы. Эти элементы будут добавлены к объекту либо с помощью `obj.append(item)`, либо пакетно с помощью `obj.extend(list_of_items)`. В основном используется для подклассов списка, но может использоваться и другими классами, если у них есть методы `append()` и `extend()` с подходящей сигнатурой. (То, используется ли `append()` или `extend()`, зависит от используемой версии протокола pickle и количества добавляемых элементов, поэтому оба должны поддерживаться.)356- Необязательно: итератор (не последовательность), возвращающий последовательные пары ключ-значение. Эти элементы будут сохранены в объекте с помощью `obj[key] = value`. В основном это используется для подклассов словаря, но может использоваться и другими классами, если они реализуют [`__setitem__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__setitem__).357- Необязательно: вызываемый объект с сигнатурой `(obj, state)`. Этот вызываемый объект позволяет пользователю программно управлять поведением обновления состояния конкретного объекта, вместо использования статического метода [`__setstate__()`](https://python-all.ru/3.8/library/pickle.html#object.__setstate__) объекта `obj`. Если не указан `None`, этот вызываемый объект будет иметь приоритет над [`__setstate__()`](https://python-all.ru/3.8/library/pickle.html#object.__setstate__) метода `obj`.358359  Новое в версии 3.8: Добавлен необязательный шестой элемент кортежа, `(obj, state)`.360361#### `object.__reduce_ex__(protocol)`362363В качестве альтернативы может быть определён метод [`__reduce_ex__()`](https://python-all.ru/3.8/library/pickle.html#object.__reduce_ex__). Единственное отличие состоит в том, что этот метод должен принимать один целочисленный аргумент – версию протокола. Если он определён, pickle будет предпочитать его методу [`__reduce__()`](https://python-all.ru/3.8/library/pickle.html#object.__reduce__). Кроме того, [`__reduce__()`](https://python-all.ru/3.8/library/pickle.html#object.__reduce__) автоматически становится синонимом расширенной версии. Основное назначение этого метода – предоставлять обратно совместимые reduce-значения для старых версий Python.364365### Персистентность внешних объектов366367Для обеспечения персистентности объектов модуль [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle) поддерживает понятие ссылки на объект вне упакованного потока данных. Такие объекты идентифицируются по постоянному идентификатору (persistent ID), который должен быть либо строкой из буквенно-цифровых символов (для протокола 0), [5](https://python-all.ru/3.8/library/pickle.html#id11) либо просто произвольным объектом (для любого более нового протокола).368369Разрешение таких постоянных идентификаторов не определяется модулем [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle); он делегирует это разрешение определённым пользователем методам упаковщика и распаковщика, соответственно [`persistent_id()`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler.persistent_id) и [`persistent_load()`](https://python-all.ru/3.8/library/pickle.html#pickle.Unpickler.persistent_load).370371Чтобы упаковать объекты, имеющие внешний постоянный идентификатор, упаковщик должен иметь пользовательский метод [`persistent_id()`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler.persistent_id), который принимает объект в качестве аргумента и возвращает либо `None`, либо постоянный идентификатор для этого объекта. Когда возвращается `None`, упаковщик просто упаковывает объект как обычно. Когда возвращается строка постоянного идентификатора, упаковщик упаковывает этот объект вместе с маркером, чтобы распаковщик распознал его как постоянный идентификатор.372373Чтобы распаковать внешние объекты, распаковщик должен иметь пользовательский метод [`persistent_load()`](https://python-all.ru/3.8/library/pickle.html#pickle.Unpickler.persistent_load), который принимает объект постоянного идентификатора и возвращает ссылочный объект.374375Вот исчерпывающий пример, показывающий, как постоянный идентификатор может использоваться для упаковки внешних объектов по ссылке.376377```python378# Простой пример, показывающий, как с помощью persistent ID можно упаковать внешние объекты по ссылке.379# внешние объекты по ссылке.380381import pickle382import sqlite3383from collections import namedtuple384385# Простой класс, представляющий запись в нашей базе данных.386MemoRecord = namedtuple("MemoRecord", "key, task")387388class DBPickler(pickle.Pickler):389390    def persistent_id(self, obj):391        # Вместо упаковки MemoRecord как обычного экземпляра класса, мы выводим392        # постоянный идентификатор.393        if isinstance(obj, MemoRecord):394            # Здесь наш постоянный идентификатор – это просто кортеж, содержащий метку и ключ, который ссылается на определённую запись в базе данных.395            # ключ, который ссылается на определённую запись в базе данных.396            return ("MemoRecord", obj.key)397        else:398            # Этот метод вызывается при каждом обнаружении постоянного идентификатора.399            # требуется упаковать как обычно.400            return None401402class DBUnpickler(pickle.Unpickler):403404    def __init__(self, file, connection):405        super().__init__(file)406        self.connection = connection407408    def persistent_load(self, pid):409        # Извлеките запись из базы данных по ссылке и верните её.410        # Всегда возбуждает исключение, если невозможно вернуть правильный объект.411        cursor = self.connection.cursor()412        type_tag, key_id = pid413        if type_tag == "MemoRecord":414            # Извлеките указанную запись из базы данных и верните её.415            cursor.execute("SELECT * FROM memos WHERE key=?", (str(key_id),))416            key, task = cursor.fetchone()417            return MemoRecord(key, task)418        else:419            # Инициализируйте и наполните базу данных.420            # Извлеките записи для упаковки.421            # по постоянному идентификатору.422            raise pickle.UnpicklingError("unsupported persistent object")423424def main():425    import io426    import pprint427428    # Обновите запись для надёжности.429    conn = sqlite3.connect(":memory:")430    cursor = conn.cursor()431    cursor.execute("CREATE TABLE memos(key INTEGER PRIMARY KEY, task TEXT)")432    tasks = (433        'give food to fish',434        'prepare group meeting',435        'fight with a zebra',436        )437    for task in tasks:438        cursor.execute("INSERT INTO memos VALUES(NULL, ?)", (task,))439440    # Извлеките записи, которые требуется упаковать.441    cursor.execute("SELECT * FROM memos")442    memos = [MemoRecord(key, task) for key, task in cursor]443    # Выводит и нумерует строки в текстовом файле.444    file = io.BytesIO()445    DBPickler(file).dump(memos)446447    print("Pickled records:")448    pprint.pprint(memos)449450    # Обновите запись, для верности.451    cursor.execute("UPDATE memos SET task='learn italian' WHERE key=1")452453    # Удаляет не подлежащие упаковке записи.454    file.seek(0)455    memos = DBUnpickler(file, conn).load()456457    print("Unpickled records:")458    pprint.pprint(memos)459460if __name__ == '__main__':461    main()462```463464### Диспетчерские таблицы465466Если требуется настроить упаковку некоторых классов, не затрагивая другой код, зависящий от упаковки, можно создать упаковщик с частной диспетчерской таблицей.467468Глобальная диспетчерская таблица, управляемая модулем [`copyreg`](https://python-all.ru/3.8/library/copyreg.html#module-copyreg), доступна как `copyreg.dispatch_table`. Поэтому можно использовать модифицированную копию `copyreg.dispatch_table` в качестве частной диспетчерской таблицы.469470Например471472```python473f = io.BytesIO()474p = pickle.Pickler(f)475p.dispatch_table = copyreg.dispatch_table.copy()476p.dispatch_table[SomeClass] = reduce_SomeClass477```478479создаёт экземпляр [`pickle.Pickler`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler) с частной диспетчерской таблицей, которая обрабатывает класс `SomeClass` особым образом. В качестве альтернативы код480481```python482class MyPickler(pickle.Pickler):483    dispatch_table = copyreg.dispatch_table.copy()484    dispatch_table[SomeClass] = reduce_SomeClass485f = io.BytesIO()486p = MyPickler(f)487```488489делает то же самое, но все экземпляры `MyPickler` по умолчанию используют одну и ту же таблицу диспетчеризации. Эквивалентный код с использованием модуля [`copyreg`](https://python-all.ru/3.8/library/copyreg.html#module-copyreg):490491```python492copyreg.pickle(SomeClass, reduce_SomeClass)493f = io.BytesIO()494p = pickle.Pickler(f)495```496497### Обработка объектов с состоянием498499Вот пример, показывающий, как изменить поведение упаковки для класса. Класс `TextReader` открывает текстовый файл и возвращает номер строки и её содержимое каждый раз, когда вызывается его метод `readline()`. Если экземпляр `TextReader` упаковывается, сохраняются все атрибуты, *кроме* элемента-файлового объекта. Когда экземпляр распаковывается, файл открывается заново, и чтение продолжается с последней позиции. Методы [`__setstate__()`](https://python-all.ru/3.8/library/pickle.html#object.__setstate__) и [`__getstate__()`](https://python-all.ru/3.8/library/pickle.html#object.__getstate__) используются для реализации такого поведения.500501```python502class TextReader:503    """Вывести и пронумеровать строки в текстовом файле."""504505    def __init__(self, filename):506        self.filename = filename507        self.file = open(filename)508        self.lineno = 0509510    def readline(self):511        self.lineno += 1512        line = self.file.readline()513        if not line:514            return None515        if line.endswith('\n'):516            line = line[:-1]517        return "%i: %s" % (self.lineno, line)518519    def __getstate__(self):520        # Восстанавливает состояние ранее открытого файла. Для этого необходимо открыть его заново и читать, пока не будет восстановлено количество строк.521        # Наконец, сохраняет файл.522        # метод, чтобы избежать изменения исходного состояния.523        state = self.__dict__.copy()524        # Для любого другого объекта используйте обычную редукцию.525        del state['file']526        return state527528    def __setstate__(self, state):529        # Восстановить атрибуты экземпляра (например, filename и lineno).530        self.__dict__.update(state)531        # Получить дескриптор исходного буферного объекта.532        # открыть его заново и читать, пока количество строк не восстановится.533        file = open(self.filename)534        for _ in range(self.lineno):535            file.readline()536        # Наконец, сохраните файл.537        self.file = file538```539540Пример использования может выглядеть так:541542```python543>>> reader = TextReader("hello.txt")544>>> reader.readline()545'1: Hello world!'546>>> reader.readline()547'2: I am line number two.'548>>> new_reader = pickle.loads(pickle.dumps(reader))549>>> new_reader.readline()550'3: Goodbye!'551```552553## Настраиваемая редукция для типов, функций и других объектов554555Новое в версии 3.8.556557Иногда [`dispatch_table`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler.dispatch_table) может быть недостаточно гибким. В частности, может потребоваться настроить упаковку на основе другого критерия, а не только типа объекта, или настроить упаковку функций и классов.558559Для таких случаев можно создать подкласс класса [`Pickler`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler) и реализовать метод [`reducer_override()`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler.reducer_override). Этот метод может возвращать произвольный кортеж редукции (см. [`__reduce__()`](https://python-all.ru/3.8/library/pickle.html#object.__reduce__)). Кроме того, он может возвращать `NotImplemented` для возврата к традиционному поведению.560561Если определены оба метода [`dispatch_table`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler.dispatch_table) и [`reducer_override()`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler.reducer_override), то приоритет имеет метод [`reducer_override()`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler.reducer_override).562563> **Примечание**564>565> Из соображений производительности [`reducer_override()`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler.reducer_override) может не вызываться для следующих объектов: `None`, `True`, `False`, а также для точных экземпляров [`int`](https://python-all.ru/3.8/library/functions.html#int), [`float`](https://python-all.ru/3.8/library/functions.html#float), [`bytes`](https://python-all.ru/3.8/library/stdtypes.html#bytes), [`str`](https://python-all.ru/3.8/library/stdtypes.html#str), [`dict`](https://python-all.ru/3.8/library/stdtypes.html#dict), [`set`](https://python-all.ru/3.8/library/stdtypes.html#set), [`frozenset`](https://python-all.ru/3.8/library/stdtypes.html#frozenset), [`list`](https://python-all.ru/3.8/library/stdtypes.html#list) и [`tuple`](https://python-all.ru/3.8/library/stdtypes.html#tuple).566567Вот простой пример, где разрешается упаковка и восстановление заданного класса:568569```python570import io571import pickle572573class MyClass:574    my_attribute = 1575576class MyPickler(pickle.Pickler):577    def reducer_override(self, obj):578        """Пользовательский редуктор для MyClass."""579        if getattr(obj, "__name__", None) == "MyClass":580            return type, (obj.__name__, obj.__bases__,581                          {'my_attribute': obj.my_attribute})582        else:583            # Для любого другого объекта используется обычная редукция.584            return NotImplemented585586f = io.BytesIO()587p = MyPickler(f)588p.dump(MyClass)589590del MyClass591592unpickled_class = pickle.loads(f.getvalue())593594assert isinstance(unpickled_class, type)595assert unpickled_class.__name__ == "MyClass"596assert unpickled_class.my_attribute == 1597```598599## Буферы внеполосной передачи600601Новое в версии 3.8.602603В некоторых контекстах модуль [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle) используется для передачи больших объёмов данных. Поэтому может быть важно минимизировать количество копирований памяти, чтобы сохранить производительность и расход ресурсов. Однако нормальная работа модуля [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle), преобразующего графоподобную структуру объектов в последовательный поток байтов, по своей сути включает копирование данных в поток pickle и из него.604605Этого ограничения можно избежать, если и *поставщик* (реализация типов объектов для передачи), и *потребитель* (реализация системы связи) поддерживают механизмы внеполосной передачи, предоставляемые протоколом pickle версии 5 и выше.606607### API поставщика608609Крупные объекты данных, подлежащие упаковке, должны реализовывать метод [`__reduce_ex__()`](https://python-all.ru/3.8/library/pickle.html#object.__reduce_ex__), специализированный для протокола 5 и выше, который возвращает экземпляр [`PickleBuffer`](https://python-all.ru/3.8/library/pickle.html#pickle.PickleBuffer) (вместо, например, объекта [`bytes`](https://python-all.ru/3.8/library/stdtypes.html#bytes)) для любых больших данных.610611Объект [`PickleBuffer`](https://python-all.ru/3.8/library/pickle.html#pickle.PickleBuffer) *сигнализирует*, что соответствующий буфер пригоден для внеполосной передачи данных. Эти объекты остаются совместимыми с обычным использованием модуля [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle). Однако потребители также могут согласиться сообщить [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle), что они будут обрабатывать эти буферы самостоятельно.612613### API потребителя614615Система связи может включить настраиваемую обработку объектов [`PickleBuffer`](https://python-all.ru/3.8/library/pickle.html#pickle.PickleBuffer), создаваемых при сериализации графа объектов.616617На стороне отправителя необходимо передать аргумент *buffer\_callback* в [`Pickler`](https://python-all.ru/3.8/library/pickle.html#pickle.Pickler) (или в функцию [`dump()`](https://python-all.ru/3.8/library/pickle.html#pickle.dump) или [`dumps()`](https://python-all.ru/3.8/library/pickle.html#pickle.dumps)), который будет вызываться для каждого [`PickleBuffer`](https://python-all.ru/3.8/library/pickle.html#pickle.PickleBuffer), генерируемого при упаковке графа объектов. Буферы, накопленные *buffer\_callback*, не будут скопированы в поток pickle – будет вставлен только дешёвый маркер.618619На стороне получателя необходимо передать аргумент *buffers* в [`Unpickler`](https://python-all.ru/3.8/library/pickle.html#pickle.Unpickler) (или в функцию [`load()`](https://python-all.ru/3.8/library/pickle.html#pickle.load) или [`loads()`](https://python-all.ru/3.8/library/pickle.html#pickle.loads)), который является итерируемым объектом с буферами, переданными в *buffer\_callback*. Этот итерируемый объект должен выдавать буферы в том же порядке, в котором они были переданы в *buffer\_callback*. Эти буферы предоставят данные, ожидаемые восстановителями объектов, чья упаковка породила исходные объекты [`PickleBuffer`](https://python-all.ru/3.8/library/pickle.html#pickle.PickleBuffer).620621Между отправляющей и принимающей сторонами система связи может реализовать собственный механизм передачи для внеполосных буферов. Потенциальные оптимизации включают использование общей памяти или сжатия, зависящего от типа данных.622623### Пример624625Вот тривиальный пример, где мы реализуем подкласс [`bytearray`](https://python-all.ru/3.8/library/stdtypes.html#bytearray), способный участвовать во внеполосной упаковке буферов:626627```python628class ZeroCopyByteArray(bytearray):629630    def __reduce_ex__(self, protocol):631        if protocol >= 5:632            return type(self)._reconstruct, (PickleBuffer(self),), None633        else:634            # PickleBuffer запрещён для протоколов pickle <= 4.635            return type(self)._reconstruct, (bytearray(self),)636637    @classmethod638    def _reconstruct(cls, obj):639        with memoryview(obj) as m:640            # Получить дескриптор исходного буферного объекта.641            obj = m.obj642            if type(obj) is cls:643                # Исходный буферный объект – это ZeroCopyByteArray, вернуть его.644                # как есть.645                return obj646            else:647                return cls(obj)648```649650Восстановитель (метод класса `_reconstruct`) возвращает объект, предоставляющий буфер, если он имеет правильный тип. Это простой способ имитировать поведение с нулевым копированием в этом игрушечном примере.651652На стороне потребителя мы можем упаковать эти объекты обычным способом, который при десериализации даст нам копию исходного объекта:653654```python655b = ZeroCopyByteArray(b"abc")656data = pickle.dumps(b, protocol=5)657new_b = pickle.loads(data)658print(b == new_b)  # True659print(b is new_b)  # False: создана копия660```661662Но если мы передадим *buffer\_callback*, а затем вернём накопленные буферы при десериализации, мы сможем получить обратно исходный объект:663664```python665b = ZeroCopyByteArray(b"abc")666buffers = []667data = pickle.dumps(b, protocol=5, buffer_callback=buffers.append)668new_b = pickle.loads(data, buffers=buffers)669print(b == new_b)  # True670print(b is new_b)  # True: копия не создана671```672673Этот пример ограничен тем, что [`bytearray`](https://python-all.ru/3.8/library/stdtypes.html#bytearray) выделяет свою собственную память: нельзя создать экземпляр [`bytearray`](https://python-all.ru/3.8/library/stdtypes.html#bytearray), основанный на памяти другого объекта. Однако сторонние типы данных, такие как массивы NumPy, не имеют этого ограничения и позволяют использовать упаковку с нулевым копированием (или с минимальным количеством копий) при передаче между разными процессами или системами.674675> **См. также**676>677> [**PEP 574**](https://python-all.ru/3.8/library/pickle.html) – Протокол pickle 5 с внеполосными данными678679## Ограничение глобалов680681По умолчанию распаковщик импортирует любой класс или функцию, найденные в данных pickle. Для многих приложений такое поведение неприемлемо, поскольку оно позволяет распаковщику импортировать и вызывать произвольный код. Просто подумайте, что делает этот вручную созданный поток данных pickle при загрузке:682683```python684>>> import pickle685>>> pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.")686hello world6870688```689690В этом примере распаковщик импортирует функцию [`os.system()`](https://python-all.ru/3.8/library/os.html#os.system), а затем применяет строковый аргумент «echo hello world». Хотя этот пример безобиден, нетрудно представить такой, который может нанести вред вашей системе.691692По этой причине может потребоваться контролировать, что распаковывается, с помощью настройки [`Unpickler.find_class()`](https://python-all.ru/3.8/library/pickle.html#pickle.Unpickler.find_class). Вопреки своему названию, [`Unpickler.find_class()`](https://python-all.ru/3.8/library/pickle.html#pickle.Unpickler.find_class) вызывается всякий раз, когда запрашивается глобальный объект (т.е. класс или функция). Таким образом, можно либо полностью запретить глобалы, либо ограничить их безопасным подмножеством.693694Вот пример распаковщика, который разрешает загрузку только нескольких безопасных классов из модуля [`builtins`](https://python-all.ru/3.8/library/builtins.html#module-builtins):695696```python697import builtins698import io699import pickle700701safe_builtins = {702    'range',703    'complex',704    'set',705    'frozenset',706    'slice',707}708709class RestrictedUnpickler(pickle.Unpickler):710711    def find_class(self, module, name):712        # Разрешать только безопасные классы из builtins.713        if module == "builtins" and name in safe_builtins:714            return getattr(builtins, name)715        # Запрещать всё остальное.716        raise pickle.UnpicklingError("global '%s.%s' is forbidden" %717                                     (module, name))718719def restricted_loads(s):720    """Вспомогательная функция, аналогичная pickle.loads()."""721    return RestrictedUnpickler(io.BytesIO(s)).load()722```723724Пример использования нашего десериализатора, работающего как задумано:725726```python727>>> restricted_loads(pickle.dumps([1, 2, range(15)]))728[1, 2, range(0, 15)]729>>> restricted_loads(b"cos\nsystem\n(S'echo hello world'\ntR.")730Traceback (most recent call last):731  ...732pickle.UnpicklingError: global 'os.system' is forbidden733>>> restricted_loads(b'cbuiltins\neval\n'734...                  b'(S\'getattr(__import__("os"), "system")'735...                  b'("echo hello world")\'\ntR.')736Traceback (most recent call last):737  ...738pickle.UnpicklingError: global 'builtins.eval' is forbidden739```740741Как показывают наши примеры, нужно быть осторожным с тем, что разрешается распаковывать. Поэтому если безопасность важна, стоит рассмотреть альтернативы, такие как marshalling API в модуле [`xmlrpc.client`](https://python-all.ru/3.8/library/xmlrpc.client.html#module-xmlrpc.client) или сторонние решения.742743## Производительность744745Последние версии протокола pickle (начиная с протокола 2) поддерживают эффективное бинарное кодирование для ряда распространённых возможностей и встроенных типов. Кроме того, модуль [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle) имеет прозрачный оптимизатор, написанный на C.746747## Примеры748749Для простейшего кода используйте функции [`dump()`](https://python-all.ru/3.8/library/pickle.html#pickle.dump) и [`load()`](https://python-all.ru/3.8/library/pickle.html#pickle.load).750751```python752import pickle753754# Произвольный набор объектов, поддерживаемых pickle.755data = {756    'a': [1, 2.0, 3, 4+6j],757    'b': ("character string", b"byte string"),758    'c': {None, True, False}759}760761with open('data.pickle', 'wb') as f:762    # Запаковать словарь 'data', используя самый высокий доступный протокол.763    pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)764```765766Следующий пример читает полученные сериализованные данные.767768```python769import pickle770771with open('data.pickle', 'rb') as f:772    # Версия используемого протокола определяется автоматически, поэтому773    # указывать её не нужно.774    data = pickle.load(f)775```776777> **См. также**778>779> **Модуль [`copyreg`](https://python-all.ru/3.8/library/copyreg.html#module-copyreg)**780>781> Регистрация конструктора интерфейса pickle для расширенных типов.782>783> **Модуль [`pickletools`](https://python-all.ru/3.8/library/pickletools.html#module-pickletools)**784>785> Инструменты для работы и анализа сериализованных данных.786>787> **Модуль [`shelve`](https://python-all.ru/3.8/library/shelve.html#module-shelve)**788>789> Индексированные базы данных объектов; использует [`pickle`](https://python-all.ru/3.8/library/pickle.html#module-pickle).790>791> **Модуль [`copy`](https://python-all.ru/3.8/library/copy.html#module-copy)**792>793> Поверхностное и глубокое копирование объектов.794>795> **Модуль [`marshal`](https://python-all.ru/3.8/library/marshal.html#module-marshal)**796>797> Высокопроизводительная сериализация встроенных типов.798799Сноски800801**[1](https://python-all.ru/3.8/library/pickle.html#id1)**802803Не путайте это с модулем [`marshal`](https://python-all.ru/3.8/library/marshal.html#module-marshal)804805**[2](https://python-all.ru/3.8/library/pickle.html#id3)**806807Вот почему функции [`lambda`](https://python-all.ru/3.8/reference/expressions.html#lambda) нельзя сериализовать: все функции `lambda` имеют одно и то же имя: `<lambda>`.808809**[3](https://python-all.ru/3.8/library/pickle.html#id4)**810811Возникающее исключение, скорее всего, будет [`ImportError`](https://python-all.ru/3.8/library/exceptions.html#ImportError) или [`AttributeError`](https://python-all.ru/3.8/library/exceptions.html#AttributeError), но может быть и другим.812813**[4](https://python-all.ru/3.8/library/pickle.html#id5)**814815Модуль [`copy`](https://python-all.ru/3.8/library/copy.html#module-copy) использует этот протокол для операций поверхностного и глубокого копирования.816817**[5](https://python-all.ru/3.8/library/pickle.html#id6)**818819Ограничение на алфавитно-цифровые символы связано с тем, что постоянные идентификаторы в протоколе 0 разделяются символом новой строки. Поэтому если в постоянных идентификаторах встречаются какие-либо символы новой строки, результирующий pickle станет нечитаемым.820