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

pickle.md

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

1> **Источник:** https://python-all.ru/3.3/library/pickle.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 12.1. [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle) – сериализация объектов Python89Модуль [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle) реализует двоичные протоколы для сериализации и десериализации структуры объектов Python. *“Pickling”* – это процесс преобразования иерархии объектов Python в поток байтов, а *“unpickling”* – обратная операция, при которой поток байтов (из [*двоичного файла*](https://python-all.ru/3.3/glossary.html#term-binary-file) или [*байтоподобного объекта*](https://python-all.ru/3.3/glossary.html#term-bytes-like-object)) преобразуется обратно в иерархию объектов. Pickling (и unpickling) также называют «сериализацией», «маршалингом»[\[1\]](https://python-all.ru/3.3/library/pickle.html#id5) или «уплощением»; однако во избежание путаницы здесь используются термины «pickling» и «unpickling».1011> **Предупреждение**12>13> Модуль [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle) не предназначен для защиты от ошибочных или вредоносных данных. Никогда не распаковывайте данные из ненадёжного или неаутентифицированного источника.1415## 12.1.1. Взаимосвязь с другими модулями Python1617### 12.1.1.1. Сравнение с `marshal`1819В Python есть более примитивный модуль сериализации под названием [`marshal`](https://python-all.ru/3.3/library/marshal.html#module-marshal), но в целом [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle) всегда должен быть предпочтительным способом сериализации объектов Python. [`marshal`](https://python-all.ru/3.3/library/marshal.html#module-marshal) существует в основном для поддержки файлов `.pyc`.2021Модуль [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle) отличается от [`marshal`](https://python-all.ru/3.3/library/marshal.html#module-marshal) в нескольких важных аспектах:2223- Модуль [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle) отслеживает уже сериализованные объекты, чтобы последующие ссылки на тот же объект не сериализовались повторно. [`marshal`](https://python-all.ru/3.3/library/marshal.html#module-marshal) этого не делает.2425  Это имеет значение как для рекурсивных объектов, так и для совместного использования объектов. Рекурсивные объекты – это объекты, содержащие ссылки на самих себя. Они не обрабатываются модулем marshal; более того, попытка выполнить маршалинг рекурсивных объектов с помощью marshal приведёт к аварийному завершению интерпретатора Python. Совместное использование объектов возникает, когда на один и тот же объект есть несколько ссылок в разных местах сериализуемой иерархии. [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle) сохраняет такие объекты только один раз и гарантирует, что все остальные ссылки указывают на главную копию. Совместно используемые объекты остаются совместно используемыми, что может быть очень важно для изменяемых объектов.26- [`marshal`](https://python-all.ru/3.3/library/marshal.html#module-marshal) нельзя использовать для сериализации пользовательских классов и их экземпляров. [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle) может прозрачно сохранять и восстанавливать экземпляры классов, однако определение класса должно быть импортируемым и находиться в том же модуле, что и на момент сохранения объекта.27- Формат сериализации [`marshal`](https://python-all.ru/3.3/library/marshal.html#module-marshal) не гарантирует переносимости между версиями Python. Поскольку его основная задача – поддержка файлов `.pyc`, разработчики Python оставляют за собой право изменять формат сериализации способами, несовместимыми с предыдущими версиями, если возникнет необходимость. Формат сериализации [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle) гарантированно обратно совместим во всех выпусках Python.2829### 12.1.1.2. Сравнение с `json`3031Существуют принципиальные различия между протоколами pickle и [JSON (JavaScript Object Notation)](https://python-all.ru/3.3/library/pickle.html):3233- JSON – это текстовый формат сериализации (он выводит текст в Unicode, хотя в большинстве случаев затем кодируется в `utf-8`), в то время как pickle – это двоичный формат сериализации.34- JSON удобочитаем, а pickle – нет;35- JSON интероперабелен и широко используется за пределами экосистемы Python, тогда как pickle является специфичным для Python;36- JSON по умолчанию может представлять лишь подмножество встроенных типов Python и не поддерживает пользовательские классы; pickle же может представлять огромное количество типов Python (многие из них автоматически, благодаря умелому использованию средств интроспекции Python; сложные случаи можно решить, реализовав [*специальные объектные API*](https://python-all.ru/3.3/library/pickle.html#pickle-inst)).3738> **См. также**39>40> Модуль [`json`](https://python-all.ru/3.3/library/json.html#module-json): модуль стандартной библиотеки, позволяющий выполнять сериализацию и десериализацию JSON.4142## 12.1.2. Формат потока данных4344Формат данных, используемый [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle), является специфичным для Python. Преимущество в том, что нет ограничений, налагаемых внешними стандартами, такими как JSON или XDR (которые не могут представлять разделение указателей); однако это означает, что программы не на Python могут не смочь восстановить сериализованные с помощью pickle объекты Python.4546По умолчанию формат данных [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle) использует относительно компактное двоичное представление. Если требуется оптимальный размер, можно эффективно [*сжимать*](https://python-all.ru/3.3/library/archiving.html) сериализованные данные.4748Модуль [`pickletools`](https://python-all.ru/3.3/library/pickletools.html#module-pickletools) содержит инструменты для анализа потоков данных, создаваемых модулем [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle). Исходный код [`pickletools`](https://python-all.ru/3.3/library/pickletools.html#module-pickletools) содержит обширные комментарии о кодах операций, используемых протоколами pickle.4950В настоящее время существует 4 различных протокола для упаковки.5152- Протокол версии 0 – это исходный «человекочитаемый» протокол, обратно совместимый с более ранними версиями Python.53- Протокол версии 1 – это старый двоичный формат, также совместимый с более ранними версиями Python.54- Протокол версии 2 был представлен в Python 2.3. Он обеспечивает гораздо более эффективную сериализацию [*классов нового стиля*](https://python-all.ru/3.3/glossary.html#term-new-style-class). Дополнительную информацию об улучшениях, внесённых протоколом 2, см. в [**PEP 307**](https://python-all.ru/3.3/library/pickle.html).55- Протокол версии 3 был добавлен в Python 3. Он явно поддерживает объекты [`bytes`](https://python-all.ru/3.3/library/functions.html#bytes) и не может быть распакован Python 2.x. Это протокол по умолчанию и текущий рекомендуемый; используйте его по возможности.5657> **Примечание**58>59> Сериализация – это более примитивное понятие, чем персистентность; хотя [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle) читает и записывает файловые объекты, он не решает проблему именования персистентных объектов, равно как и (ещё более сложную) проблему одновременного доступа к персистентным объектам. Модуль [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle) может преобразовывать сложный объект в поток байтов и обратно в объект с той же внутренней структурой. Самое очевидное применение этих потоков байтов – запись в файл, но их также можно передавать по сети или хранить в базе данных. Модуль [`shelve`](https://python-all.ru/3.3/library/shelve.html#module-shelve) предоставляет простой интерфейс для сериализации (pickle и unpickle) объектов в файлах баз данных стиля DBM.6061## 12.1.3. Интерфейс модуля6263Для сериализации иерархии объектов достаточно вызвать функцию [`dumps()`](https://python-all.ru/3.3/library/pickle.html#pickle.dumps). Аналогично, для десериализации потока данных вызывается функция [`loads()`](https://python-all.ru/3.3/library/pickle.html#pickle.loads). Однако если требуется более тонкий контроль над сериализацией и десериализацией, можно создать соответственно объект [`Pickler`](https://python-all.ru/3.3/library/pickle.html#pickle.Pickler) или [`Unpickler`](https://python-all.ru/3.3/library/pickle.html#pickle.Unpickler).6465Модуль [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle) предоставляет следующие константы:6667#### `pickle.HIGHEST_PROTOCOL`6869Самая высокая доступная версия протокола. Это значение можно передать как аргумент *protocol*.7071#### `pickle.DEFAULT_PROTOCOL`7273Протокол по умолчанию, используемый для упаковки. Может быть меньше HIGHEST\_PROTOCOL. В настоящее время протокол по умолчанию – 3, новый протокол, разработанный для Python 3.0.7475Модуль [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle) предоставляет следующие функции для упрощения процесса упаковки (pickling):7677#### `pickle.dump(obj, file, protocol=None, *, fix_imports=True)`7879Записывает упакованное (pickled) представление объекта *obj* в открытый [*файловый объект*](https://python-all.ru/3.3/glossary.html#term-file-object) *file*. Это эквивалентно вызову `Pickler(file, protocol).dump(obj)`.8081Необязательный аргумент *protocol* указывает упаковщику использовать заданный протокол; поддерживаются протоколы 0, 1, 2, 3. Протокол по умолчанию – 3; это обратно несовместимый протокол, разработанный для Python 3.0.8283Указание отрицательной версии протокола выбирает самую высокую поддерживаемую версию протокола. Чем выше используемый протокол, тем более новая версия Python требуется для чтения созданного pickle.8485Аргумент *file* должен иметь метод write(), принимающий один аргумент типа bytes. Таким образом, это может быть файл на диске, открытый для бинарной записи, экземпляр [`io.BytesIO`](https://python-all.ru/3.3/library/io.html#io.BytesIO) или любой другой пользовательский объект, соответствующий этому интерфейсу.8687Если *fix\_imports* равен true и *protocol* меньше 3, pickle попытается сопоставить новые имена Python 3.x со старыми именами модулей, используемыми в Python 2.x, чтобы поток данных pickle можно было прочитать в Python 2.x.8889#### `pickle.dumps(obj, protocol=None, *, fix_imports=True)`9091Возвращает сериализованное представление объекта в виде объекта [`bytes`](https://python-all.ru/3.3/library/functions.html#bytes) вместо записи в файл.9293Необязательный аргумент *protocol* указывает упаковщику использовать заданный протокол; поддерживаются протоколы 0, 1, 2, 3. Протокол по умолчанию – 3; это обратно несовместимый протокол, разработанный для Python 3.0.9495Указание отрицательной версии протокола выбирает самую высокую поддерживаемую версию протокола. Чем выше используемый протокол, тем более новая версия Python требуется для чтения созданного pickle.9697Если *fix\_imports* равен true и *protocol* меньше 3, pickle попытается сопоставить новые имена Python 3.x со старыми именами модулей, используемыми в Python 2.x, чтобы поток данных pickle можно было прочитать в Python 2.x.9899#### `pickle.load(file, *, fix_imports=True, encoding="ASCII", errors="strict")`100101Читает сериализованное представление объекта из открытого [*файлового объекта*](https://python-all.ru/3.3/glossary.html#term-file-object) *file* и возвращает восстановленную иерархию объектов, указанную в нём. Это эквивалентно `Unpickler(file).load()`.102103Версия протокола pickle определяется автоматически, поэтому аргумент protocol не требуется. Байты, следующие за представлением сериализованного объекта, игнорируются.104105Аргумент *file* должен иметь два метода: read(), принимающий целочисленный аргумент, и readline(), не требующий аргументов. Оба метода должны возвращать bytes. Таким образом, *file* может быть файлом на диске, открытым для бинарного чтения, объектом [`io.BytesIO`](https://python-all.ru/3.3/library/io.html#io.BytesIO) или любым другим пользовательским объектом, соответствующим этому интерфейсу.106107Необязательные ключевые аргументы: *fix\_imports*, *encoding* и *errors*. Они используются для управления совместимостью с потоком pickle, созданным Python 2.x. Если *fix\_imports* равен true, pickle попытается сопоставить старые имена Python 2.x с новыми именами, используемыми в Python 3.x. Аргументы *encoding* и *errors* указывают pickle, как декодировать 8-битные строки, сериализованные Python 2.x; по умолчанию они равны 'ASCII' и 'strict' соответственно.108109#### `pickle.loads(bytes_object, *, fix_imports=True, encoding="ASCII", errors="strict")`110111Читает сериализованную иерархию объектов из объекта [`bytes`](https://python-all.ru/3.3/library/functions.html#bytes) и возвращает восстановленную иерархию объектов, содержащуюся в нём.112113Версия протокола pickle определяется автоматически, поэтому аргумент protocol не требуется. Байты, следующие за представлением сериализованного объекта, игнорируются.114115Необязательные ключевые аргументы: *fix\_imports*, *encoding* и *errors*. Они используются для управления совместимостью с потоком pickle, созданным Python 2.x. Если *fix\_imports* равен true, pickle попытается сопоставить старые имена Python 2.x с новыми именами, используемыми в Python 3.x. Аргументы *encoding* и *errors* указывают pickle, как декодировать 8-битные строки, сериализованные Python 2.x; по умолчанию они равны 'ASCII' и 'strict' соответственно.116117Модуль [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle) определяет три исключения:118119#### `exception pickle.PickleError`120121Общий базовый класс для других исключений при упаковке. Наследует [`Exception`](https://python-all.ru/3.3/library/exceptions.html#Exception).122123#### `exception pickle.PicklingError`124125Исключение, возникающее при обнаружении [`Pickler`](https://python-all.ru/3.3/library/pickle.html#pickle.Pickler) объекта, который невозможно упаковать. Наследует [`PickleError`](https://python-all.ru/3.3/library/pickle.html#pickle.PickleError).126127Обратитесь к разделу [*Какие объекты можно упаковывать и распаковывать?*](https://python-all.ru/3.3/library/pickle.html#pickle-picklable), чтобы узнать, какие объекты можно упаковывать.128129#### `exception pickle.UnpicklingError`130131Исключение, возникающее при проблемах с распаковкой объекта, таких как повреждение данных или нарушение безопасности. Наследует [`PickleError`](https://python-all.ru/3.3/library/pickle.html#pickle.PickleError).132133Обратите внимание, что во время распаковки могут возбуждаться и другие исключения, включая (но не ограничиваясь) AttributeError, EOFError, ImportError и IndexError.134135Модуль [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle) экспортирует два класса: [`Pickler`](https://python-all.ru/3.3/library/pickle.html#pickle.Pickler) и [`Unpickler`](https://python-all.ru/3.3/library/pickle.html#pickle.Unpickler).136137#### `class pickle.Pickler(file, protocol=None, *, fix_imports=True)`138139Он принимает двоичный файл для записи потока данных pickle.140141Необязательный аргумент *protocol* указывает упаковщику использовать заданный протокол; поддерживаются протоколы 0, 1, 2, 3. Протокол по умолчанию – 3; это обратно несовместимый протокол, разработанный для Python 3.0.142143Указание отрицательной версии протокола выбирает самую высокую поддерживаемую версию протокола. Чем выше используемый протокол, тем более новая версия Python требуется для чтения созданного pickle.144145Аргумент *file* должен иметь метод write(), принимающий один аргумент типа bytes. Таким образом, это может быть файл на диске, открытый для бинарной записи, экземпляр [`io.BytesIO`](https://python-all.ru/3.3/library/io.html#io.BytesIO) или любой другой пользовательский объект, соответствующий этому интерфейсу.146147Если *fix\_imports* равен true и *protocol* меньше 3, pickle попытается сопоставить новые имена Python 3.x со старыми именами модулей, используемыми в Python 2.x, чтобы поток данных pickle можно было прочитать в Python 2.x.148149#### `dump(obj)`150151Записывает представление *obj* в виде pickle в открытый объект файла, переданный в конструкторе.152153#### `persistent_id(obj)`154155По умолчанию ничего не делает. Этот метод существует, чтобы подкласс мог его переопределить.156157Если [`persistent_id()`](https://python-all.ru/3.3/library/pickle.html#pickle.Pickler.persistent_id) возвращает `None`, *obj* упаковывается обычным образом. Любое другое значение заставляет [`Pickler`](https://python-all.ru/3.3/library/pickle.html#pickle.Pickler) выдать это значение как постоянный идентификатор для *obj*. Смысл этого постоянного идентификатора должен быть определён [`Unpickler.persistent_load()`](https://python-all.ru/3.3/library/pickle.html#pickle.Unpickler.persistent_load). Обратите внимание, что значение, возвращаемое [`persistent_id()`](https://python-all.ru/3.3/library/pickle.html#pickle.Pickler.persistent_id), само не может иметь постоянный идентификатор.158159См. [*Постоянство внешних объектов*](https://python-all.ru/3.3/library/pickle.html#pickle-persistent) для подробностей и примеров использования.160161#### `dispatch_table`162163Таблица диспетчеризации объекта-упаковщика – это реестр *функций приведения*, которые можно объявить с помощью [`copyreg.pickle()`](https://python-all.ru/3.3/library/copyreg.html#copyreg.pickle). Это отображение, ключами которого являются классы, а значениями – функции приведения. Функция приведения принимает один аргумент соответствующего класса и должна соответствовать тому же интерфейсу, что и метод [`__reduce__()`](https://python-all.ru/3.3/library/pickle.html#object.__reduce__).164165По умолчанию объект-упаковщик не имеет атрибута [`dispatch_table`](https://python-all.ru/3.3/library/pickle.html#pickle.Pickler.dispatch_table) и вместо этого использует глобальную таблицу диспетчеризации, управляемую модулем [`copyreg`](https://python-all.ru/3.3/library/copyreg.html#module-copyreg). Однако, чтобы настроить упаковку для конкретного упаковщика, можно установить атрибут [`dispatch_table`](https://python-all.ru/3.3/library/pickle.html#pickle.Pickler.dispatch_table) в объект, подобный словарю. В качестве альтернативы, если подкласс [`Pickler`](https://python-all.ru/3.3/library/pickle.html#pickle.Pickler) имеет атрибут [`dispatch_table`](https://python-all.ru/3.3/library/pickle.html#pickle.Pickler.dispatch_table), то он будет использоваться как таблица диспетчеризации по умолчанию для экземпляров этого класса.166167См. [*Таблицы диспетчеризации*](https://python-all.ru/3.3/library/pickle.html#pickle-dispatch) в качестве примеров использования.168169Новое в версии 3.3.170171#### `fast`172173Устарело. При установке в истинное значение включает быстрый режим. Быстрый режим отключает использование memo, тем самым ускоряя процесс упаковки за счёт отсутствия лишних кодов операций PUT. Его не следует использовать с самореферентными объектами, иначе [`Pickler`](https://python-all.ru/3.3/library/pickle.html#pickle.Pickler) будет рекурсировать бесконечно.174175Используйте [`pickletools.optimize()`](https://python-all.ru/3.3/library/pickletools.html#pickletools.optimize), если нужны более компактные упаковки.176177#### `class pickle.Unpickler(file, *, fix_imports=True, encoding="ASCII", errors="strict")`178179Принимает бинарный файл для чтения потока данных pickle.180181Версия протокола pickle определяется автоматически, поэтому аргумент протокола не нужен.182183Аргумент *file* должен иметь два метода: read(), принимающий целочисленный аргумент, и readline(), не требующий аргументов. Оба метода должны возвращать bytes. Таким образом, *file* может быть файловым объектом на диске, открытым для чтения в двоичном режиме, объектом [`io.BytesIO`](https://python-all.ru/3.3/library/io.html#io.BytesIO) или любым другим пользовательским объектом, соответствующим этому интерфейсу.184185Необязательными ключевыми аргументами являются *fix\_imports*, *encoding* и *errors*, которые используются для управления совместимостью с потоками pickle, созданными Python 2.x. Если *fix\_imports* равен true, pickle попытается сопоставить старые имена Python 2.x с новыми именами, используемыми в Python 3.x. Аргументы *encoding* и *errors* указывают pickle, как декодировать 8-битные строковые экземпляры, упакованные Python 2.x; по умолчанию они равны 'ASCII' и 'strict' соответственно.186187#### `load()`188189Читает представление объекта в pickle из открытого объекта файла, переданного в конструкторе, и возвращает восстановленную иерархию объектов, указанную в нём. Байты, следующие за представлением объекта в pickle, игнорируются.190191#### `persistent_load(pid)`192193По умолчанию вызывает [`UnpicklingError`](https://python-all.ru/3.3/library/pickle.html#pickle.UnpicklingError).194195Если определён, [`persistent_load()`](https://python-all.ru/3.3/library/pickle.html#pickle.Unpickler.persistent_load) должен вернуть объект, заданный постоянным идентификатором *pid*. При обнаружении недопустимого постоянного идентификатора должно быть вызвано [`UnpicklingError`](https://python-all.ru/3.3/library/pickle.html#pickle.UnpicklingError).196197См. [*Постоянство внешних объектов*](https://python-all.ru/3.3/library/pickle.html#pickle-persistent) для подробностей и примеров использования.198199#### `find_class(module, name)`200201Импортирует модуль *module* при необходимости и возвращает из него объект с именем *name*. Аргументы *module* и *name* – строки (тип [`str`](https://python-all.ru/3.3/library/stdtypes.html#str)). Обратите внимание: вопреки своему названию, [`find_class()`](https://python-all.ru/3.3/library/pickle.html#pickle.Unpickler.find_class) также используется для поиска функций.202203Подклассы могут переопределить этот метод для получения контроля над тем, объекты какого типа и каким образом могут быть загружены, что потенциально снижает риски безопасности. См. [*Ограничение глобальных объектов*](https://python-all.ru/3.3/library/pickle.html#pickle-restrict) для подробностей.204205## 12.1.4. Какие объекты можно сериализовать и десериализовать?206207Следующие типы можно сериализовать:208209- `None`, `True` и `False`210- целые числа, числа с плавающей запятой, комплексные числа211- строки, байты, байтовые массивы212- кортежи, списки, множества и словари, содержащие только сериализуемые объекты213- Функции, определённые на верхнем уровне модуля.214- встроенные функции, определённые на верхнем уровне модуля215- классы, определённые на верхнем уровне модуля216- экземпляры таких классов, у которых [`__dict__`](https://python-all.ru/3.3/library/stdtypes.html#object.__dict__) или результат вызова [`__getstate__()`](https://python-all.ru/3.3/library/pickle.html#object.__getstate__) поддаётся пиклингу (см. раздел [*Пиклинг экземпляров классов*](https://python-all.ru/3.3/library/pickle.html#pickle-inst) для подробностей).217218Попытка запакетить (pickle) непиклируемый объект вызовет исключение [`PicklingError`](https://python-all.ru/3.3/library/pickle.html#pickle.PicklingError); при этом в базовый файл может быть уже записано некоторое количество байт. Попытка запакетить сильно рекурсивную структуру данных может превысить максимальную глубину рекурсии – в этом случае будет возбуждено [`RuntimeError`](https://python-all.ru/3.3/library/exceptions.html#RuntimeError). Можно аккуратно увеличить этот предел с помощью [`sys.setrecursionlimit()`](https://python-all.ru/3.3/library/sys.html#sys.setrecursionlimit).219220Обратите внимание, что функции (встроенные и определённые пользователем) упаковываются по «полностью квалифицированному» имени, а не по значению. Это означает, что упаковывается только имя функции вместе с именем модуля, в котором функция определена. Ни код функции, ни какие-либо её атрибуты не упаковываются. Поэтому определяющий модуль должен быть импортируемым в среде распаковки, и модуль должен содержать именованный объект, иначе будет возбуждено исключение. [\[2\]](https://python-all.ru/3.3/library/pickle.html#id6)221222Аналогично, классы пиклятся по именованной ссылке, поэтому в среде распикливания действуют те же ограничения. Обратите внимание, что ни код класса, ни его данные не пиклятся, так что в следующем примере атрибут класса `attr` не восстанавливается в среде распикливания:223224```python225class Foo:226    attr = 'A class attribute'227228picklestring = pickle.dumps(Foo)229```230231Эти ограничения объясняют, почему сериализуемые функции и классы должны быть определены на верхнем уровне модуля.232233Аналогично, при пиклинге экземпляров классов код и данные самого класса не пиклятся вместе с ними. Пиклятся только данные экземпляра. Это сделано намеренно: можно исправлять ошибки в классе или добавлять методы и при этом загружать объекты, созданные более ранней версией класса. Если предполагается использовать долгоживущие объекты, которые переживут много версий класса, стоит поместить номер версии в объекты, чтобы метод [`__setstate__()`](https://python-all.ru/3.3/library/pickle.html#object.__setstate__) класса мог выполнить подходящие преобразования.234235## 12.1.5. Сериализация экземпляров классов236237В этом разделе описываются общие механизмы, позволяющие определять, настраивать и контролировать процесс сериализации и десериализации экземпляров классов.238239В большинстве случаев для того, чтобы экземпляры можно было пиклить, дополнительный код не требуется. По умолчанию pickle извлекает класс и атрибуты экземпляра через интроспекцию. При распикливании экземпляра класса его метод [`__init__()`](https://python-all.ru/3.3/reference/datamodel.html#object.__init__) обычно *не* вызывается. Стандартное поведение сначала создаёт неинициализированный экземпляр, а затем восстанавливает сохранённые атрибуты. Следующий код показывает реализацию этого поведения:240241```python242def save(obj):243    return (obj.__class__, obj.__dict__)244245def load(cls, attributes):246    obj = cls.__new__(cls)247    obj.__dict__.update(attributes)248    return obj249```250251Классы могут изменить поведение по умолчанию, предоставив один или несколько специальных методов:252253#### `object.__getnewargs__()`254255Начиная с протокола 2, классы, реализующие метод [`__getnewargs__()`](https://python-all.ru/3.3/library/pickle.html#object.__getnewargs__), могут управлять значениями, передаваемыми методу [`__new__()`](https://python-all.ru/3.3/reference/datamodel.html#object.__new__) при распаковке. Это часто необходимо для классов, метод [`__new__()`](https://python-all.ru/3.3/reference/datamodel.html#object.__new__) которых требует аргументов.256257#### `object.__getstate__()`258259Классы могут дополнительно влиять на то, как пиклятся их экземпляры. Если класс определяет метод [`__getstate__()`](https://python-all.ru/3.3/library/pickle.html#object.__getstate__), он вызывается, и возвращённый объект пиклится как содержимое экземпляра вместо содержимого словаря экземпляра. Если метод [`__getstate__()`](https://python-all.ru/3.3/library/pickle.html#object.__getstate__) отсутствует, то [`__dict__`](https://python-all.ru/3.3/library/stdtypes.html#object.__dict__) экземпляра пиклится как обычно.260261#### `object.__setstate__(state)`262263При распикливании, если класс определяет [`__setstate__()`](https://python-all.ru/3.3/library/pickle.html#object.__setstate__), он вызывается с распикленным состоянием. В этом случае объект состояния не обязан быть словарём. В противном случае распикленное состояние должно быть словарём, и его элементы присваиваются словарю нового экземпляра.264265> **Примечание**266>267> Если [`__getstate__()`](https://python-all.ru/3.3/library/pickle.html#object.__getstate__) возвращает ложное значение, метод [`__setstate__()`](https://python-all.ru/3.3/library/pickle.html#object.__setstate__) не будет вызываться при распикливании.268269Обратитесь к разделу [*Обработка объектов с состоянием*](https://python-all.ru/3.3/library/pickle.html#pickle-state) за дополнительной информацией об использовании методов [`__getstate__()`](https://python-all.ru/3.3/library/pickle.html#object.__getstate__) и [`__setstate__()`](https://python-all.ru/3.3/library/pickle.html#object.__setstate__).270271> **Примечание**272>273> Во время распаковки для экземпляра могут вызываться некоторые методы, такие как [`__getattr__()`](https://python-all.ru/3.3/reference/datamodel.html#object.__getattr__), [`__getattribute__()`](https://python-all.ru/3.3/reference/datamodel.html#object.__getattribute__) или [`__setattr__()`](https://python-all.ru/3.3/reference/datamodel.html#object.__setattr__). Если эти методы полагаются на истинность некоторого внутреннего инварианта, тип должен реализовать [`__getnewargs__()`](https://python-all.ru/3.3/library/pickle.html#object.__getnewargs__) для установки такого инварианта; в противном случае не будут вызваны ни [`__new__()`](https://python-all.ru/3.3/reference/datamodel.html#object.__new__), ни [`__init__()`](https://python-all.ru/3.3/reference/datamodel.html#object.__init__).274275Как мы увидим, pickle не использует описанные выше методы напрямую. На самом деле эти методы являются частью протокола копирования, который реализует специальный метод [`__reduce__()`](https://python-all.ru/3.3/library/pickle.html#object.__reduce__). Протокол копирования предоставляет единый интерфейс для получения данных, необходимых для пиклинга и копирования объектов.[\[3\]](https://python-all.ru/3.3/library/pickle.html#id7)276277Хотя реализация [`__reduce__()`](https://python-all.ru/3.3/library/pickle.html#object.__reduce__) непосредственно в классах может быть мощной, она чревата ошибками. Поэтому проектировщикам классов следует по возможности использовать высокоуровневый интерфейс (т.е. [`__getnewargs__()`](https://python-all.ru/3.3/library/pickle.html#object.__getnewargs__), [`__getstate__()`](https://python-all.ru/3.3/library/pickle.html#object.__getstate__) и [`__setstate__()`](https://python-all.ru/3.3/library/pickle.html#object.__setstate__)). Однако мы покажем случаи, когда использование [`__reduce__()`](https://python-all.ru/3.3/library/pickle.html#object.__reduce__) является единственным вариантом или приводит к более эффективной упаковке, или и то, и другое.278279#### `object.__reduce__()`280281Интерфейс в настоящее время определён следующим образом. Метод [`__reduce__()`](https://python-all.ru/3.3/library/pickle.html#object.__reduce__) не принимает аргументов и должен возвращать либо строку, либо, предпочтительно, кортеж (возвращаемый объект часто называют «reduce-значением»).282283Если возвращается строка, она должна интерпретироваться как имя глобальной переменной. Это должно быть локальное имя объекта относительно его модуля; модуль pickle ищет объект в пространстве имён модуля. Такое поведение обычно полезно для одиночек (singletons).284285Если возвращается кортеж, он должен содержать от двух до пяти элементов. Необязательные элементы можно опустить или указать `None` в качестве их значения. Семантика каждого элемента по порядку:286287- Вызываемый объект, который будет вызван для создания начальной версии объекта.288- Кортеж аргументов для вызываемого объекта. Если вызываемый объект не принимает ни одного аргумента, должен быть передан пустой кортеж.289- Необязательно: состояние объекта, которое будет передано методу [`__setstate__()`](https://python-all.ru/3.3/library/pickle.html#object.__setstate__) объекта, как описано ранее. Если у объекта нет такого метода, значение должно быть словарём, и оно будет добавлено в атрибут [`__dict__`](https://python-all.ru/3.3/library/stdtypes.html#object.__dict__) объекта.290- Необязательно: итератор (не последовательность), возвращающий последовательные элементы. Эти элементы будут добавлены к объекту с помощью `obj.append(item)` или, пакетно, с помощью `obj.extend(list_of_items)`. В основном это используется для подклассов списков, но может использоваться и другими классами, если у них есть методы `append()` и `extend()` с подходящей сигнатурой. (Используется ли `append()` или `extend()`, зависит от версии протокола pickle и количества добавляемых элементов, поэтому оба должны поддерживаться.)291- Необязательно: итератор (не последовательность), возвращающий последовательные пары «ключ-значение». Эти элементы будут сохранены в объекте с помощью `obj[key] = value`. В основном это используется для подклассов словарей, но может использоваться и другими классами, если они реализуют [`__setitem__()`](https://python-all.ru/3.3/reference/datamodel.html#object.__setitem__).292293#### `object.__reduce_ex__(protocol)`294295В качестве альтернативы может быть определён метод [`__reduce_ex__()`](https://python-all.ru/3.3/library/pickle.html#object.__reduce_ex__). Единственное отличие в том, что этот метод должен принимать один целочисленный аргумент – версию протокола. Если он определён, pickle будет предпочитать его методу [`__reduce__()`](https://python-all.ru/3.3/library/pickle.html#object.__reduce__). Кроме того, [`__reduce__()`](https://python-all.ru/3.3/library/pickle.html#object.__reduce__) автоматически становится синонимом расширенной версии. Основное назначение этого метода – предоставление обратно совместимых reduce-значений для старых версий Python.296297### 12.1.5.1. Сохранение внешних объектов298299Для обеспечения сохранности объектов модуль [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle) поддерживает понятие ссылки на объект, находящийся за пределами потока данных pickle. Такие объекты ссылаются через постоянный идентификатор, который может быть либо строкой из буквенно-цифровых символов (для протокола 0) [\[4\]](https://python-all.ru/3.3/library/pickle.html#id8), либо произвольным объектом (для любого более нового протокола).300301Разрешение таких постоянных идентификаторов не определяется модулем [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle); он делегирует это разрешение определенным пользователем методам пиклера и анпиклера – [`persistent_id()`](https://python-all.ru/3.3/library/pickle.html#pickle.Pickler.persistent_id) и [`persistent_load()`](https://python-all.ru/3.3/library/pickle.html#pickle.Unpickler.persistent_load) соответственно.302303Чтобы упаковать объекты, имеющие внешний постоянный идентификатор, пиклер должен иметь пользовательский метод [`persistent_id()`](https://python-all.ru/3.3/library/pickle.html#pickle.Pickler.persistent_id), который принимает объект в качестве аргумента и возвращает либо `None`, либо постоянный идентификатор для этого объекта. Когда возвращается `None`, пиклер просто упаковывает объект как обычно. Когда возвращается строка постоянного идентификатора, пиклер упакует этот объект вместе с маркером, чтобы анпиклер распознал его как постоянный идентификатор.304305Чтобы распаковать внешние объекты, анпиклер должен иметь пользовательский метод [`persistent_load()`](https://python-all.ru/3.3/library/pickle.html#pickle.Unpickler.persistent_load), который принимает объект постоянного идентификатора и возвращает объект, на который он ссылается.306307Вот исчерпывающий пример, показывающий, как постоянный идентификатор может использоваться для упаковки внешних объектов по ссылке.308309```python310# Простой пример, показывающий, как с помощью persistent ID можно упаковать внешние объекты по ссылке.311# внешние объекты по ссылке.312313import pickle314import sqlite3315from collections import namedtuple316317# Простой класс, представляющий запись в нашей базе данных.318MemoRecord = namedtuple("MemoRecord", "key, task")319320class DBPickler(pickle.Pickler):321322    def persistent_id(self, obj):323        # Вместо упаковки MemoRecord как обычного экземпляра класса, мы выводим324        # постоянный идентификатор.325        if isinstance(obj, MemoRecord):326            # Здесь наш постоянный идентификатор – это просто кортеж, содержащий метку и ключ, который ссылается на определённую запись в базе данных.327            # ключ, который ссылается на определённую запись в базе данных.328            return ("MemoRecord", obj.key)329        else:330            # Этот метод вызывается при каждом обнаружении постоянного идентификатора.331            # требуется упаковать как обычно.332            return None333334class DBUnpickler(pickle.Unpickler):335336    def __init__(self, file, connection):337        super().__init__(file)338        self.connection = connection339340    def persistent_load(self, pid):341        # Извлеките запись из базы данных по ссылке и верните её.342        # Всегда возбуждает исключение, если невозможно вернуть правильный объект.343        cursor = self.connection.cursor()344        type_tag, key_id = pid345        if type_tag == "MemoRecord":346            # Извлеките указанную запись из базы данных и верните её.347            cursor.execute("SELECT * FROM memos WHERE key=?", (str(key_id),))348            key, task = cursor.fetchone()349            return MemoRecord(key, task)350        else:351            # Инициализируйте и наполните базу данных.352            # Извлеките записи для упаковки.353            # по постоянному идентификатору.354            raise pickle.UnpicklingError("unsupported persistent object")355356def main():357    import io358    import pprint359360    # Обновите запись для надёжности.361    conn = sqlite3.connect(":memory:")362    cursor = conn.cursor()363    cursor.execute("CREATE TABLE memos(key INTEGER PRIMARY KEY, task TEXT)")364    tasks = (365        'give food to fish',366        'prepare group meeting',367        'fight with a zebra',368        )369    for task in tasks:370        cursor.execute("INSERT INTO memos VALUES(NULL, ?)", (task,))371372    # Извлеките записи, которые требуется упаковать.373    cursor.execute("SELECT * FROM memos")374    memos = [MemoRecord(key, task) for key, task in cursor]375    # Выводит и нумерует строки в текстовом файле.376    file = io.BytesIO()377    DBPickler(file).dump(memos)378379    print("Pickled records:")380    pprint.pprint(memos)381382    # Обновите запись, для верности.383    cursor.execute("UPDATE memos SET task='learn italian' WHERE key=1")384385    # Удаляет не подлежащие упаковке записи.386    file.seek(0)387    memos = DBUnpickler(file, conn).load()388389    print("Unpickled records:")390    pprint.pprint(memos)391392if __name__ == '__main__':393    main()394```395396### 12.1.5.2. Диспетчерские таблицы397398Если требуется настроить упаковку некоторых классов, не затрагивая другой код, зависящий от упаковки, можно создать упаковщик с частной диспетчерской таблицей.399400Глобальная диспетчерская таблица, управляемая модулем [`copyreg`](https://python-all.ru/3.3/library/copyreg.html#module-copyreg), доступна как `copyreg.dispatch_table`. Следовательно, можно использовать изменённую копию `copyreg.dispatch_table` в качестве частной диспетчерской таблицы.401402Например403404```python405f = io.BytesIO()406p = pickle.Pickler(f)407p.dispatch_table = copyreg.dispatch_table.copy()408p.dispatch_table[SomeClass] = reduce_SomeClass409```410411создаёт экземпляр [`pickle.Pickler`](https://python-all.ru/3.3/library/pickle.html#pickle.Pickler) с частной диспетчерской таблицей, которая обрабатывает класс `SomeClass` особым образом. В качестве альтернативы код412413```python414class MyPickler(pickle.Pickler):415    dispatch_table = copyreg.dispatch_table.copy()416    dispatch_table[SomeClass] = reduce_SomeClass417f = io.BytesIO()418p = MyPickler(f)419```420421делает то же самое, но все экземпляры `MyPickler` по умолчанию будут использовать одну и ту же диспетчерскую таблицу. Эквивалентный код с использованием модуля [`copyreg`](https://python-all.ru/3.3/library/copyreg.html#module-copyreg):422423```python424copyreg.pickle(SomeClass, reduce_SomeClass)425f = io.BytesIO()426p = pickle.Pickler(f)427```428429### 12.1.5.3. Обработка объектов с состоянием430431Вот пример, показывающий, как изменить поведение упаковки для класса. Класс `TextReader` открывает текстовый файл и возвращает номер строки и её содержимое каждый раз при вызове его метода `readline()`. Если экземпляр `TextReader` упаковывается, сохраняются все атрибуты, *кроме* члена файлового объекта. Когда экземпляр распаковывается, файл открывается заново, и чтение продолжается с последней позиции. Для реализации такого поведения используются методы [`__setstate__()`](https://python-all.ru/3.3/library/pickle.html#object.__setstate__) и [`__getstate__()`](https://python-all.ru/3.3/library/pickle.html#object.__getstate__).432433```python434class TextReader:435    """Вывести и пронумеровать строки в текстовом файле."""436437    def __init__(self, filename):438        self.filename = filename439        self.file = open(filename)440        self.lineno = 0441442    def readline(self):443        self.lineno += 1444        line = self.file.readline()445        if not line:446            return None447        if line.endswith('\n'):448            line = line[:-1]449        return "%i: %s" % (self.lineno, line)450451    def __getstate__(self):452        # Восстанавливает состояние ранее открытого файла. Для этого необходимо открыть его заново и читать, пока не будет восстановлено количество строк.453        # Наконец, сохраняет файл.454        # метод, чтобы избежать изменения исходного состояния.455        state = self.__dict__.copy()456        # Для любого другого объекта используйте обычную редукцию.457        del state['file']458        return state459460    def __setstate__(self, state):461        # Восстановить атрибуты экземпляра (например, filename и lineno).462        self.__dict__.update(state)463        # Получить дескриптор исходного буферного объекта.464        # открыть его заново и читать, пока количество строк не восстановится.465        file = open(self.filename)466        for _ in range(self.lineno):467            file.readline()468        # Наконец, сохраните файл.469        self.file = file470```471472Пример использования может выглядеть так:473474```python475>>> reader = TextReader("hello.txt")476>>> reader.readline()477'1: Hello world!'478>>> reader.readline()479'2: I am line number two.'480>>> new_reader = pickle.loads(pickle.dumps(reader))481>>> new_reader.readline()482'3: Goodbye!'483```484485## 12.1.6. Ограничение глобальных объектов486487По умолчанию распаковщик импортирует любой класс или функцию, найденные в данных pickle. Для многих приложений такое поведение неприемлемо, поскольку оно позволяет распаковщику импортировать и вызывать произвольный код. Просто подумайте, что делает этот вручную созданный поток данных pickle при загрузке:488489```python490>>> import pickle491>>> pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.")492hello world4930494```495496В этом примере анпиклер импортирует функцию [`os.system()`](https://python-all.ru/3.3/library/os.html#os.system), а затем применяет строковый аргумент «echo hello world». Хотя этот пример безобиден, нетрудно представить такой, который может повредить систему.497498По этой причине можно управлять тем, что распаковывается, настроив [`Unpickler.find_class()`](https://python-all.ru/3.3/library/pickle.html#pickle.Unpickler.find_class). Вопреки своему названию, [`Unpickler.find_class()`](https://python-all.ru/3.3/library/pickle.html#pickle.Unpickler.find_class) вызывается всякий раз, когда запрашивается глобальный объект (т. е. класс или функция). Таким образом, можно либо полностью запретить глобальные объекты, либо ограничить их безопасным подмножеством.499500Вот пример анпиклера, который разрешает загрузку только нескольких безопасных классов из модуля [`builtins`](https://python-all.ru/3.3/library/builtins.html#module-builtins):501502```python503import builtins504import io505import pickle506507safe_builtins = {508    'range',509    'complex',510    'set',511    'frozenset',512    'slice',513}514515class RestrictedUnpickler(pickle.Unpickler):516517    def find_class(self, module, name):518        # Разрешать только безопасные классы из builtins.519        if module == "builtins" and name in safe_builtins:520            return getattr(builtins, name)521        # Запрещать всё остальное.522        raise pickle.UnpicklingError("global '%s.%s' is forbidden" %523                                     (module, name))524525def restricted_loads(s):526    """Вспомогательная функция, аналогичная pickle.loads()."""527    return RestrictedUnpickler(io.BytesIO(s)).load()528```529530Пример использования нашего десериализатора, работающего как задумано:531532```python533>>> restricted_loads(pickle.dumps([1, 2, range(15)]))534[1, 2, range(0, 15)]535>>> restricted_loads(b"cos\nsystem\n(S'echo hello world'\ntR.")536Traceback (most recent call last):537  ...538pickle.UnpicklingError: global 'os.system' is forbidden539>>> restricted_loads(b'cbuiltins\neval\n'540...                  b'(S\'getattr(__import__("os"), "system")'541...                  b'("echo hello world")\'\ntR.')542Traceback (most recent call last):543  ...544pickle.UnpicklingError: global 'builtins.eval' is forbidden545```546547Как показывают наши примеры, нужно быть осторожным с тем, что разрешается распаковывать. Поэтому если безопасность вызывает беспокойство, возможно, стоит рассмотреть альтернативы, например API маршалинга в [`xmlrpc.client`](https://python-all.ru/3.3/library/xmlrpc.client.html#module-xmlrpc.client) или сторонние решения.548549## 12.1.7. Производительность550551Последние версии протокола pickle (начиная с протокола 2 и выше) содержат эффективные двоичные кодировки для нескольких распространённых возможностей и встроенных типов. Кроме того, модуль [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle) имеет прозрачный оптимизатор, написанный на C.552553## 12.1.8. Примеры554555Для простейшего кода используйте функции [`dump()`](https://python-all.ru/3.3/library/pickle.html#pickle.dump) и [`load()`](https://python-all.ru/3.3/library/pickle.html#pickle.load).556557```python558import pickle559560# Произвольный набор объектов, поддерживаемых pickle.561data = {562    'a': [1, 2.0, 3, 4+6j],563    'b': ("character string", b"byte string"),564    'c': set([None, True, False])565}566567with open('data.pickle', 'wb') as f:568    # Запаковать словарь 'data', используя самый высокий доступный протокол.569    pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)570```571572Следующий пример читает полученные сериализованные данные.573574```python575import pickle576577with open('data.pickle', 'rb') as f:578    # Версия используемого протокола определяется автоматически, поэтому579    # указывать её не нужно.580    data = pickle.load(f)581```582583> **См. также**584>585> **Модуль [`copyreg`](https://python-all.ru/3.3/library/copyreg.html#module-copyreg)**586>587> Регистрация конструктора интерфейса pickle для расширенных типов.588>589> **Модуль [`pickletools`](https://python-all.ru/3.3/library/pickletools.html#module-pickletools)**590>591> Инструменты для работы и анализа сериализованных данных.592>593> **Модуль [`shelve`](https://python-all.ru/3.3/library/shelve.html#module-shelve)**594>595> Индексированные базы данных объектов; использует596>597> [`pickle`](https://python-all.ru/3.3/library/pickle.html#module-pickle)598>599> .600>601> **Модуль [`copy`](https://python-all.ru/3.3/library/copy.html#module-copy)**602>603> Поверхностное и глубокое копирование объектов.604>605> **Модуль [`marshal`](https://python-all.ru/3.3/library/marshal.html#module-marshal)**606>607> Высокопроизводительная сериализация встроенных типов.608609Сноски610611| [\[1\]](https://python-all.ru/3.3/library/pickle.html#id1) | Не путайте это с модулем [`marshal`](https://python-all.ru/3.3/library/marshal.html#module-marshal) |612| --- | --- |613614| [\[2\]](https://python-all.ru/3.3/library/pickle.html#id2) | Возникающее исключение, скорее всего, будет [`ImportError`](https://python-all.ru/3.3/library/exceptions.html#ImportError) или [`AttributeError`](https://python-all.ru/3.3/library/exceptions.html#AttributeError), но может быть и другим. |615| --- | --- |616617| [\[3\]](https://python-all.ru/3.3/library/pickle.html#id3) | Модуль [`copy`](https://python-all.ru/3.3/library/copy.html#module-copy) использует этот протокол для операций поверхностного и глубокого копирования. |618| --- | --- |619620| [\[4\]](https://python-all.ru/3.3/library/pickle.html#id4) | Ограничение на алфавитно-цифровые символы связано с тем, что постоянные идентификаторы в протоколе 0 разделяются символом новой строки. Поэтому если в постоянных идентификаторах встречаются какие-либо символы новой строки, результирующий pickle станет нечитаемым. |621| --- | --- |622