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

pickle.md

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

1> **Источник:** https://python-all.ru/3.2/library/pickle.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 11.1. [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle) – сериализация объектов Python89Модуль [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle) реализует фундаментальный, но мощный алгоритм для сериализации и десериализации структуры объектов Python. «Pickling» – это процесс, при котором иерархия объектов Python преобразуется в поток байтов, а «unpickling» – обратная операция, при которой поток байтов преобразуется обратно в иерархию объектов. Pickling (и unpickling) также известны как «сериализация», «маршалинг», [\[1\]](https://python-all.ru/3.2/library/pickle.html#id5) или «уплощение», однако во избежание путаницы здесь используются термины «pickling» и «unpickling».1011> **Предупреждение**12>13> Модуль [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle) не предназначен для защиты от ошибочных или вредоносных данных. Никогда не распаковывайте данные из ненадёжного или неаутентифицированного источника.1415## 11.1.1. Отношение к другим модулям Python1617Модуль [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle) имеет прозрачный оптимизатор (`_pickle`), написанный на C. Он используется везде, где доступен. В противном случае используется чистая реализация на Python.1819В Python есть более примитивный модуль сериализации под названием [`marshal`](https://python-all.ru/3.2/library/marshal.html#module-marshal), но в целом [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle) всегда должен быть предпочтительным способом сериализации объектов Python. [`marshal`](https://python-all.ru/3.2/library/marshal.html#module-marshal) существует в основном для поддержки файлов `.pyc`.2021Модуль [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle) отличается от [`marshal`](https://python-all.ru/3.2/library/marshal.html#module-marshal) в нескольких важных аспектах:2223- Модуль [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle) отслеживает уже сериализованные объекты, чтобы последующие ссылки на тот же объект не сериализовались повторно. [`marshal`](https://python-all.ru/3.2/library/marshal.html#module-marshal) этого не делает.2425  Это имеет значение как для рекурсивных объектов, так и для совместного использования объектов. Рекурсивные объекты – это объекты, содержащие ссылки на самих себя. Они не обрабатываются модулем marshal; более того, попытка выполнить маршалинг рекурсивных объектов с помощью marshal приведёт к аварийному завершению интерпретатора Python. Совместное использование объектов возникает, когда на один и тот же объект есть несколько ссылок в разных местах сериализуемой иерархии. [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle) сохраняет такие объекты только один раз и гарантирует, что все остальные ссылки указывают на главную копию. Совместно используемые объекты остаются совместно используемыми, что может быть очень важно для изменяемых объектов.26- [`marshal`](https://python-all.ru/3.2/library/marshal.html#module-marshal) нельзя использовать для сериализации пользовательских классов и их экземпляров. [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle) может прозрачно сохранять и восстанавливать экземпляры классов, однако определение класса должно быть импортируемым и находиться в том же модуле, что и на момент сохранения объекта.27- Формат сериализации [`marshal`](https://python-all.ru/3.2/library/marshal.html#module-marshal) не гарантирует переносимости между версиями Python. Поскольку его основная задача – поддержка файлов `.pyc`, разработчики Python оставляют за собой право изменять формат сериализации способами, несовместимыми с предыдущими версиями, если возникнет необходимость. Формат сериализации [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle) гарантированно обратно совместим во всех выпусках Python.2829Обратите внимание, что сериализация – это более примитивное понятие, чем сохранение (persistence); хотя [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle) читает и записывает файловые объекты, он не решает проблему именования сохраняемых объектов, ни (ещё более сложную) проблему параллельного доступа к сохраняемым объектам. Модуль [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle) может преобразовать сложный объект в поток байтов и преобразовать поток байтов обратно в объект с той же внутренней структурой. Возможно, самое очевидное применение этим потокам байтов – запись их в файл, но также можно передать их по сети или сохранить в базе данных. Модуль [`shelve`](https://python-all.ru/3.2/library/shelve.html#module-shelve) предоставляет простой интерфейс для упаковки (pickle) и распаковки (unpickle) объектов в файлах баз данных типа DBM.3031## 11.1.2. Формат потока данных3233Формат данных, используемый [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle), является специфичным для Python. Преимущество в том, что нет ограничений, налагаемых внешними стандартами, такими как JSON или XDR (которые не могут представлять разделение указателей); однако это означает, что программы не на Python могут не смочь восстановить сериализованные с помощью pickle объекты Python.3435По умолчанию формат данных [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle) использует относительно компактное двоичное представление. Если требуется оптимальный размер, можно эффективно [*сжимать*](https://python-all.ru/3.2/library/archiving.html) сериализованные данные.3637Модуль [`pickletools`](https://python-all.ru/3.2/library/pickletools.html#module-pickletools) содержит инструменты для анализа потоков данных, создаваемых модулем [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle). Исходный код [`pickletools`](https://python-all.ru/3.2/library/pickletools.html#module-pickletools) содержит обширные комментарии о кодах операций, используемых протоколами pickle.3839В настоящее время существует 4 различных протокола для упаковки.4041- Протокол версии 0 – это исходный «человекочитаемый» протокол, обратно совместимый с более ранними версиями Python.42- Протокол версии 1 – это старый двоичный формат, также совместимый с более ранними версиями Python.43- Протокол версии 2 был представлен в Python 2.3. Он обеспечивает гораздо более эффективную сериализацию [*классов нового стиля*](https://python-all.ru/3.2/glossary.html#term-new-style-class). Дополнительную информацию об улучшениях, внесённых протоколом 2, см. в [**PEP 307**](https://python-all.ru/3.2/library/pickle.html).44- Протокол версии 3 был добавлен в Python 3. Он явно поддерживает объекты [`bytes`](https://python-all.ru/3.2/library/functions.html#bytes) и не может быть распакован Python 2.x. Это протокол по умолчанию и текущий рекомендуемый; используйте его по возможности.4546## 11.1.3. Интерфейс модуля4748Для сериализации иерархии объектов достаточно вызвать функцию [`dumps()`](https://python-all.ru/3.2/library/pickle.html#pickle.dumps). Аналогично, для десериализации потока данных вызывается функция [`loads()`](https://python-all.ru/3.2/library/pickle.html#pickle.loads). Однако если требуется более тонкий контроль над сериализацией и десериализацией, можно создать соответственно объект [`Pickler`](https://python-all.ru/3.2/library/pickle.html#pickle.Pickler) или [`Unpickler`](https://python-all.ru/3.2/library/pickle.html#pickle.Unpickler).4950Модуль [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle) предоставляет следующие константы:5152#### `pickle.HIGHEST_PROTOCOL`5354Самая высокая доступная версия протокола. Это значение можно передать как аргумент *protocol*.5556#### `pickle.DEFAULT_PROTOCOL`5758Протокол по умолчанию, используемый для упаковки. Может быть меньше HIGHEST\_PROTOCOL. В настоящее время протокол по умолчанию – 3, новый протокол, разработанный для Python 3.0.5960Модуль [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle) предоставляет следующие функции для упрощения процесса упаковки (pickling):6162#### `pickle.dump(obj, file, protocol=None, *, fix_imports=True)`6364Записывает упакованное (pickled) представление объекта *obj* в открытый [*файловый объект*](https://python-all.ru/3.2/glossary.html#term-file-object) *file*. Это эквивалентно вызову `Pickler(file, protocol).dump(obj)`.6566Необязательный аргумент *protocol* указывает упаковщику использовать заданный протокол; поддерживаются протоколы 0, 1, 2, 3. Протокол по умолчанию – 3; это обратно несовместимый протокол, разработанный для Python 3.0.6768Указание отрицательной версии протокола выбирает самую высокую поддерживаемую версию протокола. Чем выше используемый протокол, тем более новая версия Python требуется для чтения созданного pickle.6970Аргумент *file* должен иметь метод write(), принимающий один аргумент типа bytes. Таким образом, это может быть файл на диске, открытый для бинарной записи, экземпляр [`io.BytesIO`](https://python-all.ru/3.2/library/io.html#io.BytesIO) или любой другой пользовательский объект, соответствующий этому интерфейсу.7172Если *fix\_imports* равен True и *протокол* меньше 3, pickle попытается отобразить новые имена Python 3.x на старые имена модулей, используемые в Python 2.x, чтобы поток данных pickle был читаем в Python 2.x.7374#### `pickle.dumps(obj, protocol=None, *, fix_imports=True)`7576Возвращает сериализованное представление объекта в виде объекта [`bytes`](https://python-all.ru/3.2/library/functions.html#bytes) вместо записи в файл.7778Необязательный аргумент *protocol* указывает упаковщику использовать заданный протокол; поддерживаются протоколы 0, 1, 2, 3. Протокол по умолчанию – 3; это обратно несовместимый протокол, разработанный для Python 3.0.7980Указание отрицательной версии протокола выбирает самую высокую поддерживаемую версию протокола. Чем выше используемый протокол, тем более новая версия Python требуется для чтения созданного pickle.8182Если *fix\_imports* равен True и *протокол* меньше 3, pickle попытается отобразить новые имена Python 3.x на старые имена модулей, используемые в Python 2.x, чтобы поток данных pickle был читаем в Python 2.x.8384#### `pickle.load(file, *, fix_imports=True, encoding="ASCII", errors="strict")`8586Читает сериализованное представление объекта из открытого [*файлового объекта*](https://python-all.ru/3.2/glossary.html#term-file-object) *file* и возвращает восстановленную иерархию объектов, указанную в нём. Это эквивалентно `Unpickler(file).load()`.8788Версия протокола pickle определяется автоматически, поэтому аргумент protocol не требуется. Байты, следующие за представлением сериализованного объекта, игнорируются.8990Аргумент *file* должен иметь два метода: read(), принимающий целочисленный аргумент, и readline(), не требующий аргументов. Оба метода должны возвращать bytes. Таким образом, *file* может быть файлом на диске, открытым для бинарного чтения, объектом [`io.BytesIO`](https://python-all.ru/3.2/library/io.html#io.BytesIO) или любым другим пользовательским объектом, соответствующим этому интерфейсу.9192Необязательными именованными аргументами являются *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' соответственно.9394#### `pickle.loads(bytes_object, *, fix_imports=True, encoding="ASCII", errors="strict")`9596Читает сериализованную иерархию объектов из объекта [`bytes`](https://python-all.ru/3.2/library/functions.html#bytes) и возвращает восстановленную иерархию объектов, содержащуюся в нём.9798Версия протокола pickle определяется автоматически, поэтому аргумент protocol не требуется. Байты, следующие за представлением сериализованного объекта, игнорируются.99100Необязательными именованными аргументами являются *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' соответственно.101102Модуль [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle) определяет три исключения:103104#### `exception pickle.PickleError`105106Общий базовый класс для других исключений при упаковке. Наследует [`Exception`](https://python-all.ru/3.2/library/exceptions.html#Exception).107108#### `exception pickle.PicklingError`109110Исключение, возникающее при обнаружении [`Pickler`](https://python-all.ru/3.2/library/pickle.html#pickle.Pickler) объекта, который невозможно упаковать. Наследует [`PickleError`](https://python-all.ru/3.2/library/pickle.html#pickle.PickleError).111112Обратитесь к разделу [*Какие объекты можно упаковывать и распаковывать?*](https://python-all.ru/3.2/library/pickle.html#pickle-picklable), чтобы узнать, какие объекты можно упаковывать.113114#### `exception pickle.UnpicklingError`115116Исключение, возникающее при проблемах с распаковкой объекта, таких как повреждение данных или нарушение безопасности. Наследует [`PickleError`](https://python-all.ru/3.2/library/pickle.html#pickle.PickleError).117118Обратите внимание, что во время распаковки могут возбуждаться и другие исключения, включая (но не ограничиваясь) AttributeError, EOFError, ImportError и IndexError.119120Модуль [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle) экспортирует два класса: [`Pickler`](https://python-all.ru/3.2/library/pickle.html#pickle.Pickler) и [`Unpickler`](https://python-all.ru/3.2/library/pickle.html#pickle.Unpickler).121122#### `class pickle.Pickler(file, protocol=None, *, fix_imports=True)`123124Он принимает двоичный файл для записи потока данных pickle.125126Необязательный аргумент *protocol* указывает упаковщику использовать заданный протокол; поддерживаются протоколы 0, 1, 2, 3. Протокол по умолчанию – 3; это обратно несовместимый протокол, разработанный для Python 3.0.127128Указание отрицательной версии протокола выбирает самую высокую поддерживаемую версию протокола. Чем выше используемый протокол, тем более новая версия Python требуется для чтения созданного pickle.129130Аргумент *file* должен иметь метод write(), принимающий один аргумент типа bytes. Таким образом, это может быть файл на диске, открытый для бинарной записи, экземпляр [`io.BytesIO`](https://python-all.ru/3.2/library/io.html#io.BytesIO) или любой другой пользовательский объект, соответствующий этому интерфейсу.131132Если *fix\_imports* равен True и *протокол* меньше 3, pickle попытается отобразить новые имена Python 3.x на старые имена модулей, используемые в Python 2.x, чтобы поток данных pickle был читаем в Python 2.x.133134#### `dump(obj)`135136Записывает представление *obj* в виде pickle в открытый объект файла, переданный в конструкторе.137138#### `persistent_id(obj)`139140По умолчанию ничего не делает. Этот метод существует, чтобы подкласс мог его переопределить.141142Если [`persistent_id()`](https://python-all.ru/3.2/library/pickle.html#pickle.Pickler.persistent_id) возвращает `None`, *obj* упаковывается обычным образом. Любое другое значение заставляет [`Pickler`](https://python-all.ru/3.2/library/pickle.html#pickle.Pickler) выдать это значение как постоянный идентификатор для *obj*. Смысл этого постоянного идентификатора должен быть определён [`Unpickler.persistent_load()`](https://python-all.ru/3.2/library/pickle.html#pickle.Unpickler.persistent_load). Обратите внимание, что значение, возвращаемое [`persistent_id()`](https://python-all.ru/3.2/library/pickle.html#pickle.Pickler.persistent_id), само не может иметь постоянный идентификатор.143144См. [*Постоянство внешних объектов*](https://python-all.ru/3.2/library/pickle.html#pickle-persistent) для подробностей и примеров использования.145146#### `fast`147148Устарело. При установке в истинное значение включает быстрый режим. Быстрый режим отключает использование memo, тем самым ускоряя процесс упаковки за счёт отсутствия лишних кодов операций PUT. Его не следует использовать с самореферентными объектами, иначе [`Pickler`](https://python-all.ru/3.2/library/pickle.html#pickle.Pickler) будет рекурсировать бесконечно.149150Используйте [`pickletools.optimize()`](https://python-all.ru/3.2/library/pickletools.html#pickletools.optimize), если нужны более компактные упаковки.151152#### `class pickle.Unpickler(file, *, fix_imports=True, encoding="ASCII", errors="strict")`153154Принимает бинарный файл для чтения потока данных pickle.155156Версия протокола pickle определяется автоматически, поэтому аргумент протокола не нужен.157158Аргумент *file* должен иметь два метода: read(), принимающий целочисленный аргумент, и readline(), не требующий аргументов. Оба метода должны возвращать bytes. Таким образом, *file* может быть файловым объектом на диске, открытым для чтения в двоичном режиме, объектом [`io.BytesIO`](https://python-all.ru/3.2/library/io.html#io.BytesIO) или любым другим пользовательским объектом, соответствующим этому интерфейсу.159160Необязательными именованными аргументами являются *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' соответственно.161162#### `load()`163164Читает представление объекта в pickle из открытого объекта файла, переданного в конструкторе, и возвращает восстановленную иерархию объектов, указанную в нём. Байты, следующие за представлением объекта в pickle, игнорируются.165166#### `persistent_load(pid)`167168По умолчанию вызывает [`UnpicklingError`](https://python-all.ru/3.2/library/pickle.html#pickle.UnpicklingError).169170Если определён, [`persistent_load()`](https://python-all.ru/3.2/library/pickle.html#pickle.Unpickler.persistent_load) должен вернуть объект, заданный постоянным идентификатором *pid*. При обнаружении недопустимого постоянного идентификатора должно быть вызвано [`UnpicklingError`](https://python-all.ru/3.2/library/pickle.html#pickle.UnpicklingError).171172См. [*Постоянство внешних объектов*](https://python-all.ru/3.2/library/pickle.html#pickle-persistent) для подробностей и примеров использования.173174#### `find_class(module, name)`175176Импортирует модуль *module* при необходимости и возвращает из него объект с именем *name*. Аргументы *module* и *name* – строки (тип [`str`](https://python-all.ru/3.2/library/functions.html#str)). Обратите внимание: вопреки своему названию, [`find_class()`](https://python-all.ru/3.2/library/pickle.html#pickle.Unpickler.find_class) также используется для поиска функций.177178Подклассы могут переопределить этот метод для получения контроля над тем, объекты какого типа и каким образом могут быть загружены, что потенциально снижает риски безопасности. См. [*Ограничение глобальных объектов*](https://python-all.ru/3.2/library/pickle.html#pickle-restrict) для подробностей.179180## 11.1.4. Что можно упаковать и распаковать?181182Следующие типы можно сериализовать:183184- `None`, `True` и `False`185- целые числа, числа с плавающей запятой, комплексные числа186- строки, байты, байтовые массивы187- кортежи, списки, множества и словари, содержащие только сериализуемые объекты188- Функции, определённые на верхнем уровне модуля.189- встроенные функции, определённые на верхнем уровне модуля190- классы, определённые на верхнем уровне модуля191- экземпляры таких классов, чьи `__dict__` или результат вызова `__getstate__()` поддаются упаковке (см. раздел [*Упаковка экземпляров классов*](https://python-all.ru/3.2/library/pickle.html#pickle-inst) для подробностей).192193Попытка запакетить (pickle) непиклируемый объект вызовет исключение [`PicklingError`](https://python-all.ru/3.2/library/pickle.html#pickle.PicklingError); при этом в базовый файл может быть уже записано некоторое количество байт. Попытка запакетить сильно рекурсивную структуру данных может превысить максимальную глубину рекурсии – в этом случае будет возбуждено [`RuntimeError`](https://python-all.ru/3.2/library/exceptions.html#RuntimeError). Можно аккуратно увеличить этот предел с помощью [`sys.setrecursionlimit()`](https://python-all.ru/3.2/library/sys.html#sys.setrecursionlimit).194195Обратите внимание, что функции (встроенные и определённые пользователем) упаковываются по «полностью квалифицированному» имени, а не по значению. Это означает, что упаковывается только имя функции вместе с именем модуля, в котором функция определена. Ни код функции, ни какие-либо её атрибуты не упаковываются. Поэтому определяющий модуль должен быть импортируемым в среде распаковки, и модуль должен содержать именованный объект, иначе будет возбуждено исключение. [\[2\]](https://python-all.ru/3.2/library/pickle.html#id6)196197Аналогично, классы пиклятся по именованной ссылке, поэтому в среде распикливания действуют те же ограничения. Обратите внимание, что ни код класса, ни его данные не пиклятся, так что в следующем примере атрибут класса `attr` не восстанавливается в среде распикливания:198199```python200class Foo:201    attr = 'A class attribute'202203picklestring = pickle.dumps(Foo)204```205206Эти ограничения объясняют, почему сериализуемые функции и классы должны быть определены на верхнем уровне модуля.207208Аналогично, при упаковке экземпляров классов код и данные класса не упаковываются вместе с ними. Упаковываются только данные экземпляра. Это сделано намеренно, чтобы можно было исправлять ошибки в классе или добавлять методы, и при этом загружать объекты, созданные более ранней версией класса. Если предполагается использовать долгоживущие объекты, которые будут существовать на протяжении многих версий класса, возможно, стоит добавить в объекты номер версии, чтобы подходящие преобразования могли быть выполнены методом `__setstate__()` класса.209210## 11.1.5. Упаковка экземпляров классов211212В этом разделе описываются общие механизмы, позволяющие определять, настраивать и контролировать процесс сериализации и десериализации экземпляров классов.213214В большинстве случаев для того, чтобы экземпляры можно было пиклить, дополнительный код не требуется. По умолчанию pickle извлекает класс и атрибуты экземпляра через интроспекцию. При распикливании экземпляра класса его метод [`__init__()`](https://python-all.ru/3.2/reference/datamodel.html#object.__init__) обычно *не* вызывается. Стандартное поведение сначала создаёт неинициализированный экземпляр, а затем восстанавливает сохранённые атрибуты. Следующий код показывает реализацию этого поведения:215216```python217def save(obj):218    return (obj.__class__, obj.__dict__)219220def load(cls, attributes):221    obj = cls.__new__(cls)222    obj.__dict__.update(attributes)223    return obj224```225226Классы могут изменить поведение по умолчанию, предоставив один или несколько специальных методов:227228#### `object.__getnewargs__()`229230Начиная с протокола 2, классы, реализующие метод [`__getnewargs__()`](https://python-all.ru/3.2/library/pickle.html#pickle.object.__getnewargs__), могут управлять значениями, передаваемыми методу [`__new__()`](https://python-all.ru/3.2/reference/datamodel.html#object.__new__) при распаковке. Это часто необходимо для классов, метод [`__new__()`](https://python-all.ru/3.2/reference/datamodel.html#object.__new__) которых требует аргументов.231232#### `object.__getstate__()`233234Классы могут дополнительно влиять на то, как пиклятся их экземпляры. Если класс определяет метод [`__getstate__()`](https://python-all.ru/3.2/library/pickle.html#pickle.object.__getstate__), он вызывается, и возвращённый объект пиклится как содержимое экземпляра вместо содержимого словаря экземпляра. Если метод [`__getstate__()`](https://python-all.ru/3.2/library/pickle.html#pickle.object.__getstate__) отсутствует, то [`__dict__`](https://python-all.ru/3.2/library/stdtypes.html#object.__dict__) экземпляра пиклится как обычно.235236#### `object.__setstate__(state)`237238При распикливании, если класс определяет [`__setstate__()`](https://python-all.ru/3.2/library/pickle.html#pickle.object.__setstate__), он вызывается с распикленным состоянием. В этом случае объект состояния не обязан быть словарём. В противном случае распикленное состояние должно быть словарём, и его элементы присваиваются словарю нового экземпляра.239240> **Примечание**241>242> Если [`__getstate__()`](https://python-all.ru/3.2/library/pickle.html#pickle.object.__getstate__) возвращает ложное значение, метод [`__setstate__()`](https://python-all.ru/3.2/library/pickle.html#pickle.object.__setstate__) не будет вызываться при распикливании.243244Обратитесь к разделу [*Handling Stateful Objects*](https://python-all.ru/3.2/library/pickle.html#pickle-state) за дополнительной информацией об использовании методов `__getstate__()` и `__setstate__()`.245246> **Примечание**247>248> При распаковке некоторые методы, такие как [`__getattr__()`](https://python-all.ru/3.2/reference/datamodel.html#object.__getattr__), [`__getattribute__()`](https://python-all.ru/3.2/reference/datamodel.html#object.__getattribute__) или [`__setattr__()`](https://python-all.ru/3.2/reference/datamodel.html#object.__setattr__), могут вызываться для экземпляра. Если эти методы полагаются на некоторый внутренний инвариант, тип должен реализовать `__getnewargs__()` для установки такого инварианта; в противном случае не будут вызваны ни [`__new__()`](https://python-all.ru/3.2/reference/datamodel.html#object.__new__), ни [`__init__()`](https://python-all.ru/3.2/reference/datamodel.html#object.__init__).249250Как мы увидим, pickle не использует напрямую описанные выше методы. На самом деле эти методы являются частью протокола копирования, который реализует специальный метод `__reduce__()`. Протокол копирования предоставляет единый интерфейс для получения данных, необходимых для упаковки и копирования объектов. [\[3\]](https://python-all.ru/3.2/library/pickle.html#id7)251252Хотя это мощный инструмент, реализация `__reduce__()` напрямую в ваших классах чревата ошибками. По этой причине разработчикам классов следует по возможности использовать высокоуровневый интерфейс (то есть `__getnewargs__()`, `__getstate__()` и `__setstate__()`). Однако мы покажем случаи, где использование `__reduce__()` является единственным вариантом, либо приводит к более эффективной упаковке или к тому и другому.253254#### `object.__reduce__()`255256Интерфейс в настоящее время определён следующим образом. Метод [`__reduce__()`](https://python-all.ru/3.2/library/pickle.html#pickle.object.__reduce__) не принимает аргументов и должен возвращать либо строку, либо, предпочтительно, кортеж (возвращаемый объект часто называют «reduce-значением»).257258Если возвращается строка, она должна интерпретироваться как имя глобальной переменной. Это должно быть локальное имя объекта относительно его модуля; модуль pickle ищет объект в пространстве имён модуля. Такое поведение обычно полезно для одиночек (singletons).259260Если возвращается кортеж, он должен содержать от двух до пяти элементов. Необязательные элементы можно опустить или указать `None` в качестве их значения. Семантика каждого элемента по порядку:261262- Вызываемый объект, который будет вызван для создания начальной версии объекта.263- Кортеж аргументов для вызываемого объекта. Если вызываемый объект не принимает ни одного аргумента, должен быть передан пустой кортеж.264- Необязательно: состояние объекта, которое будет передано методу [`__setstate__()`](https://python-all.ru/3.2/library/pickle.html#pickle.object.__setstate__) объекта, как описано ранее. Если у объекта нет такого метода, значение должно быть словарём, и оно будет добавлено в атрибут [`__dict__`](https://python-all.ru/3.2/library/stdtypes.html#object.__dict__) объекта.265- Необязательно: итератор (не последовательность), возвращающий последовательные элементы. Эти элементы будут добавлены к объекту с помощью `obj.append(item)` или, пакетно, с помощью `obj.extend(list_of_items)`. В основном это используется для подклассов списков, но может использоваться и другими классами, если у них есть методы `append()` и `extend()` с подходящей сигнатурой. (Используется ли `append()` или `extend()`, зависит от версии протокола pickle и количества добавляемых элементов, поэтому оба должны поддерживаться.)266- Необязательно: итератор (не последовательность), возвращающий последовательные пары «ключ-значение». Эти элементы будут сохранены в объекте с помощью `obj[key] = value`. В основном это используется для подклассов словарей, но может использоваться и другими классами, если они реализуют [`__setitem__()`](https://python-all.ru/3.2/reference/datamodel.html#object.__setitem__).267268#### `object.__reduce_ex__(protocol)`269270В качестве альтернативы может быть определён метод [`__reduce_ex__()`](https://python-all.ru/3.2/library/pickle.html#pickle.object.__reduce_ex__). Единственное отличие в том, что этот метод должен принимать один целочисленный аргумент – версию протокола. Если он определён, pickle будет предпочитать его методу [`__reduce__()`](https://python-all.ru/3.2/library/pickle.html#pickle.object.__reduce__). Кроме того, [`__reduce__()`](https://python-all.ru/3.2/library/pickle.html#pickle.object.__reduce__) автоматически становится синонимом расширенной версии. Основное назначение этого метода – предоставление обратно совместимых reduce-значений для старых версий Python.271272### 11.1.5.1. Сохранение внешних объектов273274Для обеспечения сохранности объектов модуль [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle) поддерживает понятие ссылки на объект, находящийся за пределами потока данных pickle. Такие объекты ссылаются через постоянный идентификатор, который может быть либо строкой из буквенно-цифровых символов (для протокола 0) [\[4\]](https://python-all.ru/3.2/library/pickle.html#id8), либо произвольным объектом (для любого более нового протокола).275276Разрешение таких постоянных идентификаторов не определяется модулем [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle); оно делегируется определённым пользователем методам упаковщика и распаковщика, соответственно `persistent_id()` и `persistent_load()`.277278Для упаковки объектов, имеющих внешний постоянный идентификатор, упаковщик должен иметь пользовательский метод `persistent_id()`, который принимает объект в качестве аргумента и возвращает либо `None`, либо постоянный идентификатор для этого объекта. Когда возвращается `None`, упаковщик просто упаковывает объект как обычно. Когда возвращается строка постоянного идентификатора, упаковщик упакует этот объект вместе с маркером, чтобы распаковщик распознал его как постоянный идентификатор.279280Для распаковки внешних объектов распаковщик должен иметь пользовательский метод `persistent_load()`, который принимает объект постоянного идентификатора и возвращает объект, на который он ссылается.281282Вот исчерпывающий пример, показывающий, как постоянный идентификатор может использоваться для упаковки внешних объектов по ссылке.283284```python285# Простой пример, показывающий, как с помощью persistent ID можно упаковать внешние объекты по ссылке.286# внешние объекты по ссылке.287288import pickle289import sqlite3290from collections import namedtuple291292# Простой класс, представляющий запись в нашей базе данных.293MemoRecord = namedtuple("MemoRecord", "key, task")294295class DBPickler(pickle.Pickler):296297    def persistent_id(self, obj):298        # Вместо упаковки MemoRecord как обычного экземпляра класса, мы выводим299        # постоянный идентификатор.300        if isinstance(obj, MemoRecord):301            # Здесь наш постоянный идентификатор – это просто кортеж, содержащий метку и ключ, который ссылается на определённую запись в базе данных.302            # ключ, который ссылается на определённую запись в базе данных.303            return ("MemoRecord", obj.key)304        else:305            # Этот метод вызывается при каждом обнаружении постоянного идентификатора.306            # требуется упаковать как обычно.307            return None308309class DBUnpickler(pickle.Unpickler):310311    def __init__(self, file, connection):312        super().__init__(file)313        self.connection = connection314315    def persistent_load(self, pid):316        # Извлеките запись из базы данных по ссылке и верните её.317        # Всегда возбуждает исключение, если невозможно вернуть правильный объект.318        cursor = self.connection.cursor()319        type_tag, key_id = pid320        if type_tag == "MemoRecord":321            # Извлеките указанную запись из базы данных и верните её.322            cursor.execute("SELECT * FROM memos WHERE key=?", (str(key_id),))323            key, task = cursor.fetchone()324            return MemoRecord(key, task)325        else:326            # Инициализируйте и наполните базу данных.327            # Извлеките записи для упаковки.328            # по постоянному идентификатору.329            raise pickle.UnpicklingError("unsupported persistent object")330331def main():332    import io333    import pprint334335    # Обновите запись для надёжности.336    conn = sqlite3.connect(":memory:")337    cursor = conn.cursor()338    cursor.execute("CREATE TABLE memos(key INTEGER PRIMARY KEY, task TEXT)")339    tasks = (340        'give food to fish',341        'prepare group meeting',342        'fight with a zebra',343        )344    for task in tasks:345        cursor.execute("INSERT INTO memos VALUES(NULL, ?)", (task,))346347    # Извлеките записи, которые требуется упаковать.348    cursor.execute("SELECT * FROM memos")349    memos = [MemoRecord(key, task) for key, task in cursor]350    # Выводит и нумерует строки в текстовом файле.351    file = io.BytesIO()352    DBPickler(file).dump(memos)353354    print("Pickled records:")355    pprint.pprint(memos)356357    # Обновите запись, для верности.358    cursor.execute("UPDATE memos SET task='learn italian' WHERE key=1")359360    # Удаляет не подлежащие упаковке записи.361    file.seek(0)362    memos = DBUnpickler(file, conn).load()363364    print("Unpickled records:")365    pprint.pprint(memos)366367if __name__ == '__main__':368    main()369```370371### 11.1.5.2. Обработка объектов с состоянием372373Вот пример, показывающий, как изменить поведение упаковки для класса. Класс `TextReader` открывает текстовый файл и возвращает номер строки и содержимое строки каждый раз, когда вызывается его метод [`readline()`](https://python-all.ru/3.2/library/readline.html#module-readline). Если экземпляр `TextReader` упаковывается, сохраняются все атрибуты *кроме* элемента файлового объекта. При распаковке экземпляра файл открывается заново и чтение возобновляется с последнего места. Для реализации этого поведения используются методы `__setstate__()` и `__getstate__()`.374375```python376class TextReader:377    """Вывести и пронумеровать строки в текстовом файле."""378379    def __init__(self, filename):380        self.filename = filename381        self.file = open(filename)382        self.lineno = 0383384    def readline(self):385        self.lineno += 1386        line = self.file.readline()387        if not line:388            return None389        if line.endswith('\n'):390            line = line[:-1]391        return "%i: %s" % (self.lineno, line)392393    def __getstate__(self):394        # Восстанавливает состояние ранее открытого файла. Для этого необходимо открыть его заново и читать, пока не будет восстановлено количество строк.395        # Наконец, сохраняет файл.396        # метод, чтобы избежать изменения исходного состояния.397        state = self.__dict__.copy()398        # Для любого другого объекта используйте обычную редукцию.399        del state['file']400        return state401402    def __setstate__(self, state):403        # Восстановить атрибуты экземпляра (например, filename и lineno).404        self.__dict__.update(state)405        # Получить дескриптор исходного буферного объекта.406        # открыть его заново и читать, пока количество строк не восстановится.407        file = open(self.filename)408        for _ in range(self.lineno):409            file.readline()410        # Наконец, сохраните файл.411        self.file = file412```413414Пример использования может выглядеть так:415416```python417>>> reader = TextReader("hello.txt")418>>> reader.readline()419'1: Hello world!'420>>> reader.readline()421'2: I am line number two.'422>>> new_reader = pickle.loads(pickle.dumps(reader))423>>> new_reader.readline()424'3: Goodbye!'425```426427## 11.1.6. Ограничение глобалов428429По умолчанию распаковщик импортирует любой класс или функцию, найденные в данных pickle. Для многих приложений такое поведение неприемлемо, поскольку оно позволяет распаковщику импортировать и вызывать произвольный код. Просто подумайте, что делает этот вручную созданный поток данных pickle при загрузке:430431```python432>>> import pickle433>>> pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.")434hello world4350436```437438В этом примере анпиклер импортирует функцию [`os.system()`](https://python-all.ru/3.2/library/os.html#os.system), а затем применяет строковый аргумент «echo hello world». Хотя этот пример безобиден, нетрудно представить такой, который может повредить систему.439440По этой причине, возможно, вы захотите контролировать, что распаковывается, настраивая [`Unpickler.find_class()`](https://python-all.ru/3.2/library/pickle.html#pickle.Unpickler.find_class). Вопреки своему названию, `find_class()` вызывается всякий раз, когда запрашивается глобал (то есть класс или функция). Таким образом, можно либо полностью запретить глобалы, либо ограничить их безопасным подмножеством.441442Вот пример анпиклера, который разрешает загрузку только нескольких безопасных классов из модуля [`builtins`](https://python-all.ru/3.2/library/builtins.html#module-builtins):443444```python445import builtins446import io447import pickle448449safe_builtins = {450    'range',451    'complex',452    'set',453    'frozenset',454    'slice',455}456457class RestrictedUnpickler(pickle.Unpickler):458459    def find_class(self, module, name):460        # Разрешать только безопасные классы из builtins.461        if module == "builtins" and name in safe_builtins:462            return getattr(builtins, name)463        # Запрещать всё остальное.464        raise pickle.UnpicklingError("global '%s.%s' is forbidden" %465                                     (module, name))466467def restricted_loads(s):468    """Вспомогательная функция, аналогичная pickle.loads()."""469    return RestrictedUnpickler(io.BytesIO(s)).load()470```471472Пример использования нашего десериализатора, работающего как задумано:473474```python475>>> restricted_loads(pickle.dumps([1, 2, range(15)]))476[1, 2, range(0, 15)]477>>> restricted_loads(b"cos\nsystem\n(S'echo hello world'\ntR.")478Traceback (most recent call last):479  ...480pickle.UnpicklingError: global 'os.system' is forbidden481>>> restricted_loads(b'cbuiltins\neval\n'482...                  b'(S\'getattr(__import__("os"), "system")'483...                  b'("echo hello world")\'\ntR.')484Traceback (most recent call last):485  ...486pickle.UnpicklingError: global 'builtins.eval' is forbidden487```488489Как показывают наши примеры, нужно быть осторожным с тем, что разрешается распаковывать. Поэтому если безопасность вызывает беспокойство, возможно, стоит рассмотреть альтернативы, например API маршалинга в [`xmlrpc.client`](https://python-all.ru/3.2/library/xmlrpc.client.html#module-xmlrpc.client) или сторонние решения.490491## 11.1.7. Примеры492493Для простейшего кода используйте функции [`dump()`](https://python-all.ru/3.2/library/pickle.html#pickle.dump) и [`load()`](https://python-all.ru/3.2/library/pickle.html#pickle.load).494495```python496import pickle497498# Произвольный набор объектов, поддерживаемых pickle.499data = {500    'a': [1, 2.0, 3, 4+6j],501    'b': ("character string", b"byte string"),502    'c': set([None, True, False])503}504505with open('data.pickle', 'wb') as f:506    # Запаковать словарь 'data', используя самый высокий доступный протокол.507    pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)508```509510Следующий пример читает полученные сериализованные данные.511512```python513import pickle514515with open('data.pickle', 'rb') as f:516    # Версия используемого протокола определяется автоматически, поэтому517    # указывать её не нужно.518    data = pickle.load(f)519```520521> **См. также**522>523> **Модуль [`copyreg`](https://python-all.ru/3.2/library/copyreg.html#module-copyreg)**524>525> Регистрация конструктора интерфейса pickle для расширенных типов.526>527> **Модуль [`pickletools`](https://python-all.ru/3.2/library/pickletools.html#module-pickletools)**528>529> Инструменты для работы и анализа сериализованных данных.530>531> **Модуль [`shelve`](https://python-all.ru/3.2/library/shelve.html#module-shelve)**532>533> Индексированные базы данных объектов; использует534>535> [`pickle`](https://python-all.ru/3.2/library/pickle.html#module-pickle)536>537> .538>539> **Модуль [`copy`](https://python-all.ru/3.2/library/copy.html#module-copy)**540>541> Поверхностное и глубокое копирование объектов.542>543> **Модуль [`marshal`](https://python-all.ru/3.2/library/marshal.html#module-marshal)**544>545> Высокопроизводительная сериализация встроенных типов.546547Сноски548549| [\[1\]](https://python-all.ru/3.2/library/pickle.html#id1) | Не путайте это с модулем [`marshal`](https://python-all.ru/3.2/library/marshal.html#module-marshal) |550| --- | --- |551552| [\[2\]](https://python-all.ru/3.2/library/pickle.html#id2) | Возникающее исключение, скорее всего, будет [`ImportError`](https://python-all.ru/3.2/library/exceptions.html#ImportError) или [`AttributeError`](https://python-all.ru/3.2/library/exceptions.html#AttributeError), но может быть и другим. |553| --- | --- |554555| [\[3\]](https://python-all.ru/3.2/library/pickle.html#id3) | Модуль [`copy`](https://python-all.ru/3.2/library/copy.html#module-copy) использует этот протокол для операций поверхностного и глубокого копирования. |556| --- | --- |557558| [\[4\]](https://python-all.ru/3.2/library/pickle.html#id4) | Ограничение на алфавитно-цифровые символы связано с тем, что постоянные идентификаторы в протоколе 0 разделяются символом новой строки. Поэтому если в постоянных идентификаторах встречаются какие-либо символы новой строки, результирующий pickle станет нечитаемым. |559| --- | --- |560