pickle.md
1> **Источник:** https://python-all.ru/3.0/library/pickle.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# `pickle` – сериализация объектов Python89Модуль `pickle` реализует фундаментальный, но мощный алгоритм сериализации и десериализации структуры объектов Python. «Pickling» – это процесс преобразования иерархии объектов Python в поток байтов, а «unpickling» – обратная операция, при которой поток байтов преобразуется обратно в иерархию объектов. Pickling (и unpickling) также известен как «сериализация», «маршалинг» [\[1\]](https://python-all.ru/3.0/library/pickle.html#id5) или «уплощение», однако во избежание путаницы здесь используются термины «pickling» и «unpickling».1011## Отношение к другим модулям Python1213Модуль `pickle` имеет прозрачный оптимизатор (`_pickle`), написанный на C. Он используется, когда доступен. В противном случае используется реализация на чистом Python.1415В Python есть более примитивный модуль сериализации под названием [`marshal`](https://python-all.ru/3.0/library/marshal.html#module-marshal), но в целом `pickle` всегда должен быть предпочтительным способом сериализации объектов Python. [`marshal`](https://python-all.ru/3.0/library/marshal.html#module-marshal) существует в первую очередь для поддержки `.pyc`-файлов Python.1617Модуль `pickle` отличается от [`marshal`](https://python-all.ru/3.0/library/marshal.html#module-marshal) несколькими существенными аспектами:1819- Модуль `pickle` отслеживает уже сериализованные объекты, чтобы последующие ссылки на тот же объект не сериализовались повторно. [`marshal`](https://python-all.ru/3.0/library/marshal.html#module-marshal) этого не делает.2021 Это влияет как на рекурсивные объекты, так и на разделение объектов. Рекурсивные объекты – это объекты, содержащие ссылки на самих себя. Они не обрабатываются marshal, и более того, попытка маршалировать рекурсивные объекты приведёт к аварийному завершению интерпретатора Python. Разделение объектов (object sharing) происходит, когда на один и тот же объект есть несколько ссылок в разных местах сериализуемой иерархии объектов. `pickle` сохраняет такие объекты только один раз и гарантирует, что все остальные ссылки указывают на главную копию. Разделяемые объекты остаются разделяемыми, что может быть очень важно для изменяемых объектов.22- [`marshal`](https://python-all.ru/3.0/library/marshal.html#module-marshal) нельзя использовать для сериализации пользовательских классов и их экземпляров. `pickle` может прозрачно сохранять и восстанавливать экземпляры классов, однако определение класса должно быть импортируемым и находиться в том же модуле, что и при сохранении объекта.23- Формат сериализации [`marshal`](https://python-all.ru/3.0/library/marshal.html#module-marshal) не гарантирует переносимости между версиями Python. Поскольку его основная задача – поддержка файлов `.pyc`, разработчики Python оставляют за собой право изменять формат сериализации несовместимым с предыдущими версиями образом, если возникнет необходимость. Формат сериализации `pickle` гарантированно обратно совместим с разными выпусками Python.2425> **Предупреждение**26>27> Модуль `pickle` не предназначен для защиты от ошибочных или вредоносных данных. Не следует распаковывать данные, полученные из ненадёжного или непроверенного источника.2829Обратите внимание, что сериализация – это более примитивное понятие, чем сохранение (persistence); хотя `pickle` читает и записывает файловые объекты, он не решает проблему именования сохраняемых объектов, ни (ещё более сложную) проблему конкурентного доступа к сохраняемым объектам. Модуль `pickle` может преобразовать сложный объект в поток байтов и обратно – из потока байтов в объект с той же внутренней структурой. Самое очевидное, что можно сделать с этими потоками байтов – записать их в файл, но также можно передать их по сети или сохранить в базе данных. Модуль [`shelve`](https://python-all.ru/3.0/library/shelve.html#module-shelve) предоставляет простой интерфейс для упаковки (pickle) и распаковки (unpickle) объектов в файлах баз данных в стиле DBM.3031## Формат потока данных3233Формат данных, используемый `pickle`, является специфичным для Python. Это даёт преимущество: отсутствуют ограничения, накладываемые внешними стандартами, такими как XDR (который не может представлять разделение указателей); однако это означает, что программы на других языках могут не смочь восстановить упакованные (pickled) объекты Python.3435По умолчанию формат данных `pickle` использует компактное двоичное представление. Модуль [`pickletools`](https://python-all.ru/3.0/library/pickletools.html#module-pickletools) содержит инструменты для анализа потоков данных, созданных `pickle`.3637В настоящее время существует 4 различных протокола для упаковки.3839- Версия протокола 0 является оригинальным ASCII-протоколом и обратно совместима с предыдущими версиями Python.40- Версия протокола 1 – это старый двоичный формат, также совместимый с ранними версиями Python.41- Версия протокола 2 была представлена в Python 2.3. Она обеспечивает гораздо более эффективную упаковку (pickling) [*классов нового стиля*](https://python-all.ru/3.0/glossary.html#term-new-style-class).42- Версия протокола 3 была добавлена в Python 3.0. Она имеет явную поддержку байтов (bytes) и не может быть распакована модулями pickle Python 2.x. Это текущий рекомендуемый протокол, используйте его, когда это возможно.4344Смотрите [**PEP 307**](https://python-all.ru/3.0/library/pickle.html) для получения информации об улучшениях, внесённых протоколом 2. См. исходный код [`pickletools`](https://python-all.ru/3.0/library/pickletools.html#module-pickletools) для подробных комментариев об опкодах, используемых протоколами pickle.4546## Интерфейс модуля4748Чтобы сериализовать иерархию объектов, сначала создаётся объект Pickler, затем вызывается его метод [`dump()`](https://python-all.ru/3.0/library/pickle.html#pickle.dump). Для десериализации потока данных сначала создаётся Unpickler, затем вызывается его метод [`load()`](https://python-all.ru/3.0/library/pickle.html#pickle.load). Модуль `pickle` предоставляет следующую константу:4950#### `[pickle.HIGHEST_PROTOCOL]pickle.HIGHEST_PROTOCOL`5152Самая высокая доступная версия протокола. Это значение можно передать как аргумент5354*protocol*5556.5758> **Примечание**59>60> Файлы pickle, созданные с протоколами \>= 1, следует всегда открывать в бинарном режиме. Для старого ASCII-протокола pickle 0 можно использовать как текстовый, так и бинарный режим, при условии соблюдения согласованности.61>62> Файл pickle, записанный с протоколом 0 в бинарном режиме, будет содержать одиночные символы перевода строки в качестве разделителей строк и поэтому будет выглядеть «странно» при просмотре в Блокноте или других редакторах, не поддерживающих этот формат.6364#### `[pickle.DEFAULT_PROTOCOL]pickle.DEFAULT_PROTOCOL`6566Протокол, используемый по умолчанию для упаковки (pickling). Может быть меньше, чем HIGHEST\_PROTOCOL. В настоящее время протокол по умолчанию – 3; это протокол, не обратно совместимый, предназначенный для Python 3.0.6768Модуль `pickle` предоставляет следующие функции для упрощения процесса упаковки (pickling):6970#### `[pickle.dump]pickle.dump(obj, file[, protocol])`7172Записывает сериализованное представление *obj* в открытый файловый объект *file*. Это эквивалентно `Pickler(file, протокол).dump(obj)`.7374Необязательный аргумент *protocol* указывает упаковщику использовать заданный протокол; поддерживаются протоколы 0, 1, 2, 3. Протокол по умолчанию – 3; это обратно несовместимый протокол, разработанный для Python 3.0.7576Указание отрицательной версии протокола выбирает самую высокую поддерживаемую версию протокола. Чем выше используемый протокол, тем более новая версия Python требуется для чтения созданного pickle.7778Аргумент *file* должен иметь метод write(), принимающий единственный аргумент типа bytes. Таким образом, это может быть файловый объект, открытый для бинарной записи, экземпляр io.BytesIO или любой другой пользовательский объект, удовлетворяющий этому интерфейсу.7980#### `[pickle.dumps]pickle.dumps(obj[, protocol])`8182Возвращает сериализованное представление объекта в виде объекта [`bytes`](https://python-all.ru/3.0/library/functions.html#bytes) вместо записи в файл.8384Необязательный аргумент *protocol* указывает упаковщику использовать заданный протокол; поддерживаются протоколы 0, 1, 2, 3. Протокол по умолчанию – 3; это обратно несовместимый протокол, разработанный для Python 3.0.8586Указание отрицательной версии протокола выбирает самую высокую поддерживаемую версию протокола. Чем выше используемый протокол, тем более новая версия Python требуется для чтения созданного pickle.8788#### `[pickle.load]pickle.load(file[, *, encoding="ASCII", errors="strict"])`8990Читает сериализованное представление объекта из открытого файлового объекта *file* и возвращает восстановленную иерархию объектов, указанную в нём. Это эквивалентно `Unpickler(file).load()`.9192Версия протокола pickle определяется автоматически, поэтому аргумент protocol не требуется. Байты, следующие за представлением сериализованного объекта, игнорируются.9394Аргумент *file* должен иметь два метода: read(), принимающий целочисленный аргумент, и readline(), не требующий аргументов. Оба метода должны возвращать bytes. Таким образом, *file* может быть бинарным файловым объектом, открытым для чтения, объектом BytesIO или любым другим пользовательским объектом, удовлетворяющим этому интерфейсу.9596Необязательные именованные аргументы – encoding и errors, которые используются для декодирования 8-битных строковых экземпляров, сериализованных Python 2.x. По умолчанию они равны 'ASCII' и 'strict' соответственно.9798#### `[pickle.loads]pickle.loads(bytes_object[, *, encoding="ASCII", errors="strict"])`99100Читает сериализованную иерархию объектов из объекта [`bytes`](https://python-all.ru/3.0/library/functions.html#bytes) и возвращает восстановленную иерархию объектов, содержащуюся в нём.101102Версия протокола pickle определяется автоматически, поэтому аргумент protocol не требуется. Байты, следующие за представлением сериализованного объекта, игнорируются.103104Необязательные именованные аргументы – encoding и errors, которые используются для декодирования 8-битных строковых экземпляров, сериализованных Python 2.x. По умолчанию они равны 'ASCII' и 'strict' соответственно.105106Модуль `pickle` определяет три исключения:107108#### `[pickle.PickleError]exception pickle.PickleError`109110Общий базовый класс для других исключений при упаковке. Наследует111112[`Exception`](https://python-all.ru/3.0/library/exceptions.html#exceptions.Exception)113114.115116#### `[pickle.PicklingError]exception pickle.PicklingError`117118Исключение, возникающее при обнаружении [`Pickler`](https://python-all.ru/3.0/library/pickle.html#pickle.Pickler) объекта, который невозможно упаковать. Наследует [`PickleError`](https://python-all.ru/3.0/library/pickle.html#pickle.PickleError).119120Обратитесь к разделу [*Какие объекты можно упаковывать и распаковывать?*](https://python-all.ru/3.0/library/pickle.html#pickle-picklable), чтобы узнать, какие объекты можно упаковывать.121122#### `[pickle.UnpicklingError]exception pickle.UnpicklingError`123124Исключение, возникающее при проблеме распаковки (unpickling) объекта, например, при повреждении данных или нарушении безопасности. Наследует [`PickleError`](https://python-all.ru/3.0/library/pickle.html#pickle.PickleError).125126Обратите внимание, что во время распаковки могут возбуждаться и другие исключения, включая (но не ограничиваясь) AttributeError, EOFError, ImportError и IndexError.127128Модуль `pickle` экспортирует два класса: [`Pickler`](https://python-all.ru/3.0/library/pickle.html#pickle.Pickler) и [`Unpickler`](https://python-all.ru/3.0/library/pickle.html#pickle.Unpickler):129130#### `[pickle.Pickler]class pickle.Pickler(file[, protocol])`131132Он принимает двоичный файл для записи потока данных pickle.133134Необязательный аргумент *protocol* указывает упаковщику использовать заданный протокол; поддерживаются протоколы 0, 1, 2, 3. Протокол по умолчанию – 3; это обратно несовместимый протокол, разработанный для Python 3.0.135136Указание отрицательной версии протокола выбирает самую высокую поддерживаемую версию протокола. Чем выше используемый протокол, тем более новая версия Python требуется для чтения созданного pickle.137138Аргумент *file* должен иметь метод write(), принимающий единственный аргумент типа bytes. Таким образом, это может быть файловый объект, открытый для бинарной записи, экземпляр io.BytesIO или любой другой пользовательский объект, удовлетворяющий этому интерфейсу.139140#### `[pickle.Pickler.dump]dump(obj)`141142Записывает представление143144*obj*145146в виде pickle в открытый объект файла, переданный в конструкторе.147148#### `[pickle.Pickler.persistent_id]persistent_id(obj)`149150По умолчанию ничего не делает. Этот метод существует, чтобы подкласс мог его переопределить.151152Если [`persistent_id()`](https://python-all.ru/3.0/library/pickle.html#pickle.Pickler.persistent_id) возвращает `None`, *obj* упаковывается обычным образом. Любое другое значение заставляет [`Pickler`](https://python-all.ru/3.0/library/pickle.html#pickle.Pickler) выдать это значение как постоянный идентификатор для *obj*. Смысл этого постоянного идентификатора должен быть определён [`Unpickler.persistent_load()`](https://python-all.ru/3.0/library/pickle.html#pickle.Unpickler.persistent_load). Обратите внимание, что значение, возвращаемое [`persistent_id()`](https://python-all.ru/3.0/library/pickle.html#pickle.Pickler.persistent_id), само не может иметь постоянный идентификатор.153154См. [*Постоянство внешних объектов*](https://python-all.ru/3.0/library/pickle.html#pickle-persistent) для подробностей и примеров использования.155156#### `[pickle.Pickler.clear_memo]clear_memo()`157158Устарело. Вместо этого используйте метод159160`clear()`161162на163164[`memo`](https://python-all.ru/3.0/library/pickle.html#pickle.Pickler.memo)165166. Очищает memo пиклера; полезно при повторном использовании пиклеров.167168#### `[pickle.Pickler.fast]fast`169170Устарело. При установке в истинное значение включает быстрый режим. Быстрый режим отключает использование memo, тем самым ускоряя процесс упаковки за счёт отсутствия лишних кодов операций PUT. Его не следует использовать с самореферентными объектами, иначе [`Pickler`](https://python-all.ru/3.0/library/pickle.html#pickle.Pickler) будет рекурсировать бесконечно.171172Используйте [`pickletools.optimize()`](https://python-all.ru/3.0/library/pickletools.html#pickletools.optimize), если нужны более компактные упаковки.173174#### `[pickle.Pickler.memo]memo`175176Словарь, содержащий ранее сериализованные объекты, чтобы совместно используемые или рекурсивные объекты можно было сериализовать по ссылке, а не по значению.177178Можно выполнять несколько вызовов метода [`dump()`](https://python-all.ru/3.0/library/pickle.html#pickle.dump) одного и того же экземпляра [`Pickler`](https://python-all.ru/3.0/library/pickle.html#pickle.Pickler). Эти вызовы должны соответствовать такому же количеству вызовов метода [`load()`](https://python-all.ru/3.0/library/pickle.html#pickle.load) соответствующего экземпляра [`Unpickler`](https://python-all.ru/3.0/library/pickle.html#pickle.Unpickler) . Если один и тот же объект сериализуется несколькими вызовами [`dump()`](https://python-all.ru/3.0/library/pickle.html#pickle.dump), то все вызовы [`load()`](https://python-all.ru/3.0/library/pickle.html#pickle.load) будут давать ссылки на один и тот же объект.179180Обратите внимание: это предназначено для сериализации нескольких объектов без промежуточных изменений самих объектов или их частей. Если объект изменяется, а затем сериализуется повторно с использованием того же экземпляра [`Pickler`](https://python-all.ru/3.0/library/pickle.html#pickle.Pickler), объект не будет сериализован заново – будет сериализована ссылка на него, и [`Unpickler`](https://python-all.ru/3.0/library/pickle.html#pickle.Unpickler) вернёт старое значение, а не изменённое.181182#### `[pickle.Unpickler]class pickle.Unpickler(file[, *, encoding="ASCII", errors="strict"])`183184Принимает бинарный файл для чтения потока данных pickle.185186Версия протокола pickle определяется автоматически, поэтому аргумент протокола не нужен.187188Аргумент *file* должен иметь два метода: метод read(), принимающий целочисленный аргумент, и метод readline(), не требующий аргументов. Оба метода должны возвращать байты. Таким образом, *file* может быть бинарным файловым объектом, открытым для чтения, объектом BytesIO или любым другим пользовательским объектом, соответствующим данному интерфейсу.189190Необязательные именованные аргументы – encoding и errors, которые используются для декодирования 8-битных строк, сериализованных Python 2.x. По умолчанию они равны 'ASCII' и 'strict' соответственно.191192#### `[pickle.Unpickler.load]load()`193194Читает представление объекта в pickle из открытого объекта файла, переданного в конструкторе, и возвращает восстановленную иерархию объектов, указанную в нём. Байты, следующие за представлением объекта в pickle, игнорируются.195196#### `[pickle.Unpickler.persistent_load]persistent_load(pid)`197198По умолчанию вызывает `UnpickingError`.199200Если определена, [`persistent_load()`](https://python-all.ru/3.0/library/pickle.html#pickle.Unpickler.persistent_load) должна возвращать объект, заданный постоянным идентификатором *pid*. Если встречен недопустимый постоянный идентификатор, должно быть возбуждено `UnpickingError`.201202См. [*Постоянство внешних объектов*](https://python-all.ru/3.0/library/pickle.html#pickle-persistent) для подробностей и примеров использования.203204#### `[pickle.Unpickler.find_class]find_class(module, name)`205206Импортирует модуль *module* при необходимости и возвращает из него объект с именем *name*. Аргументы *module* и *name* – строки (тип [`str`](https://python-all.ru/3.0/library/functions.html#str)). Обратите внимание: вопреки своему названию, [`find_class()`](https://python-all.ru/3.0/library/pickle.html#pickle.Unpickler.find_class) также используется для поиска функций.207208Подклассы могут переопределить этот метод для получения контроля над тем, объекты какого типа и каким образом могут быть загружены, что потенциально снижает риски безопасности. См. [*Ограничение глобальных объектов*](https://python-all.ru/3.0/library/pickle.html#pickle-restrict) для подробностей.209210## Какие объекты можно сериализовать и десериализовать?211212Следующие типы можно сериализовать:213214- `None`, `True` и `False`215- целые числа, числа с плавающей запятой, комплексные числа216- строки, байты, байтовые массивы217- кортежи, списки, множества и словари, содержащие только сериализуемые объекты218- Функции, определённые на верхнем уровне модуля.219- встроенные функции, определённые на верхнем уровне модуля220- классы, определённые на верхнем уровне модуля221- экземпляры таких классов, у которых `__dict__` или `__setstate__()` могут быть упакованы (см. раздел [*Pickling Class Instances*](https://python-all.ru/3.0/library/pickle.html#pickle-inst) для подробностей)222223Попытка запакетить (pickle) непиклируемый объект вызовет исключение [`PicklingError`](https://python-all.ru/3.0/library/pickle.html#pickle.PicklingError); при этом в базовый файл может быть уже записано некоторое количество байт. Попытка запакетить сильно рекурсивную структуру данных может превысить максимальную глубину рекурсии – в этом случае будет возбуждено [`RuntimeError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.RuntimeError). Можно аккуратно увеличить этот предел с помощью [`sys.setrecursionlimit()`](https://python-all.ru/3.0/library/sys.html#sys.setrecursionlimit).224225Обратите внимание, что функции (встроенные и определяемые пользователем) упаковываются по «полному имени» (fully qualified) ссылке, а не по значению. Это означает, что упаковывается только имя функции вместе с именем модуля, в котором функция определена. Ни код функции, ни её атрибуты не упаковываются. Таким образом, определяющий модуль должен быть импортируем в среде распаковки, и модуль должен содержать именованный объект, иначе будет возбуждено исключение. [\[2\]](https://python-all.ru/3.0/library/pickle.html#id6)226227Аналогично, классы пиклятся по именованной ссылке, поэтому в среде распикливания действуют те же ограничения. Обратите внимание, что ни код класса, ни его данные не пиклятся, так что в следующем примере атрибут класса `attr` не восстанавливается в среде распикливания:228229```python230class Foo:231 attr = 'A class attribute'232233picklestring = pickle.dumps(Foo)234```235236Эти ограничения объясняют, почему сериализуемые функции и классы должны быть определены на верхнем уровне модуля.237238Аналогично, при упаковке экземпляров классов код и данные класса не упаковываются вместе с ними. Упаковываются только данные экземпляра. Это сделано намеренно, чтобы можно было исправлять ошибки в классе или добавлять методы, и при этом загружать объекты, созданные более ранней версией класса. Если предполагается использовать долгоживущие объекты, которые будут существовать на протяжении многих версий класса, возможно, стоит добавить в объекты номер версии, чтобы подходящие преобразования могли быть выполнены методом `__setstate__()` класса.239240## Сериализация экземпляров классов241242В этом разделе описываются общие механизмы, позволяющие определять, настраивать и контролировать процесс сериализации и десериализации экземпляров классов.243244В большинстве случаев для того, чтобы экземпляры можно было пиклить, дополнительный код не требуется. По умолчанию pickle извлекает класс и атрибуты экземпляра через интроспекцию. При распикливании экземпляра класса его метод [`__init__()`](https://python-all.ru/3.0/reference/datamodel.html#object.__init__) обычно *не* вызывается. Стандартное поведение сначала создаёт неинициализированный экземпляр, а затем восстанавливает сохранённые атрибуты. Следующий код показывает реализацию этого поведения:245246```python247def save(obj):248 return (obj.__class__, obj.__dict__)249250def load(cls, attributes):251 obj = cls.__new__(cls)252 obj.__dict__.update(attributes)253 return obj254```255256Классы могут изменить поведение по умолчанию, предоставив один или несколько специальных методов. В протоколе 2 и новее классы, реализующие метод `__getnewargs__()`, могут определять значения, передаваемые методу [`__new__()`](https://python-all.ru/3.0/reference/datamodel.html#object.__new__) при десериализации. Это часто требуется для классов, чей метод [`__new__()`](https://python-all.ru/3.0/reference/datamodel.html#object.__new__) требует аргументов.257258Классы могут дальше влиять на то, как их экземпляры сериализуются; если класс определяет метод `__getstate__()`, он вызывается, и возвращаемый объект сериализуется как содержимое экземпляра, вместо содержимого словаря экземпляра. Если метод `__getstate__()` отсутствует, `__dict__` экземпляра сериализуется как обычно.259260При десериализации, если класс определяет `__setstate__()`, он вызывается с десериализованным состоянием. В этом случае не требуется, чтобы объект состояния был словарем. В противном случае сериализованное состояние должно быть словарем, и его элементы присваиваются словарю нового экземпляра.261262> **Примечание**263>264> Если `__getstate__()` возвращает ложное значение, метод `__setstate__()` не будет вызван.265266Обратитесь к разделу [*Handling Stateful Objects*](https://python-all.ru/3.0/library/pickle.html#pickle-state) за дополнительной информацией об использовании методов `__getstate__()` и `__setstate__()`.267268Как мы увидим, pickle не использует напрямую описанные выше методы. На самом деле эти методы являются частью протокола копирования, который реализует специальный метод `__reduce__()`. Протокол копирования предоставляет единый интерфейс для получения данных, необходимых для упаковки и копирования объектов. [\[3\]](https://python-all.ru/3.0/library/pickle.html#id7)269270Хотя реализация `__reduce__()` напрямую в своих классах – мощный инструмент, это чревато ошибками. По этой причине разработчикам классов следует по возможности использовать высокоуровневый интерфейс (т.е. `__getnewargs__()`, `__getstate__()` и `__setstate__()`). Однако мы покажем случаи, когда использование `__reduce__()` является единственным вариантом или приводит к более эффективной сериализации (или и то, и другое).271272Интерфейс на данный момент определён следующим образом. Метод `__reduce__()` не принимает аргументов и должен возвращать либо строку, либо, что предпочтительнее, кортеж (возвращаемый объект часто называют «значением reduce»).273274Если возвращается строка, она должна интерпретироваться как имя глобальной переменной. Это должно быть локальное имя объекта относительно его модуля; модуль pickle ищет в пространстве имён модуля, чтобы определить модуль объекта. Такое поведение обычно полезно для синглтонов.275276Если возвращается кортеж, его длина должна быть от двух до пяти элементов. Необязательные элементы можно опустить или указать `None` в качестве их значения. Семантика каждого элемента перечислена по порядку:277278- Вызываемый объект, который будет вызван для создания начальной версии объекта.279- Кортеж аргументов для вызываемого объекта. Если вызываемый объект не принимает аргументов, необходимо передать пустой кортеж.280- Необязательно: состояние объекта, которое будет передано методу `__setstate__()` объекта, как описано ранее. Если у объекта нет такого метода, значение должно быть словарём, и оно будет добавлено в атрибут `__dict__` объекта.281- Необязательно: итератор (не последовательность), порождающий последовательные элементы. Эти элементы будут добавлены к объекту с помощью `obj.append(item)` либо, пакетно, с помощью `obj.extend(list_of_items)`. Это в первую очередь используется для подклассов списка, но может применяться и другими классами, если у них есть методы `append()` и `extend()` с соответствующей сигнатурой. (Будет ли использован `append()` или `extend()`, зависит от версии протокола pickle, а также от количества добавляемых элементов, поэтому оба должны поддерживаться.)282- Необязательно: итератор (не последовательность), порождающий последовательные пары ключ-значение. Эти элементы будут сохранены в объект с помощью `obj[key] = value`. Это в первую очередь используется для подклассов словаря, но может применяться и другими классами, если они реализуют [`__setitem__()`](https://python-all.ru/3.0/reference/datamodel.html#object.__setitem__).283284В качестве альтернативы может быть определён метод `__reduce_ex__()`. Единственное отличие заключается в том, что этот метод должен принимать один целочисленный аргумент – версию протокола. При его наличии pickle будет использовать его вместо метода `__reduce__()`. Кроме того, `__reduce__()` автоматически становится синонимом расширенной версии. Основное назначение этого метода – предоставление обратно совместимых значений reduce для старых версий Python.285286### Персистентность внешних объектов287288Для удобства сохранения объектов модуль `pickle` поддерживает концепцию ссылки на объект вне упакованного потока данных. Такие объекты обозначаются постоянным идентификатором, который должен быть либо строкой из буквенно-цифровых символов (для протокола 0) [\[4\]](https://python-all.ru/3.0/library/pickle.html#id8), либо произвольным объектом (для любого более нового протокола).289290Разрешение таких постоянных идентификаторов не определяется модулем `pickle`; он делегирует это разрешение определённым пользователем методам упаковщика и распаковщика – `persistent_id()` и `persistent_load()` соответственно.291292Для упаковки объектов, имеющих внешний постоянный идентификатор, упаковщик должен иметь пользовательский метод `persistent_id()`, который принимает объект в качестве аргумента и возвращает либо `None`, либо постоянный идентификатор для этого объекта. Когда возвращается `None`, упаковщик просто упаковывает объект как обычно. Когда возвращается строка постоянного идентификатора, упаковщик упакует этот объект вместе с маркером, чтобы распаковщик распознал его как постоянный идентификатор.293294Для распаковки внешних объектов распаковщик должен иметь пользовательский метод `persistent_load()`, который принимает объект постоянного идентификатора и возвращает объект, на который он ссылается.295296Вот исчерпывающий пример, показывающий, как постоянный идентификатор может использоваться для упаковки внешних объектов по ссылке.297298```python299# Простой пример, показывающий, как с помощью persistent ID можно упаковать внешние объекты по ссылке.300# внешние объекты по ссылке.301302import pickle303import sqlite3304from collections import namedtuple305306# Простой класс, представляющий запись в нашей базе данных.307MemoRecord = namedtuple("MemoRecord", "key, task")308309class DBPickler(pickle.Pickler):310311 def persistent_id(self, obj):312 # Вместо упаковки MemoRecord как обычного экземпляра класса, мы выводим313 # постоянный идентификатор.314 if isinstance(obj, MemoRecord):315 # Здесь наш постоянный идентификатор – это просто кортеж, содержащий метку и ключ, который ссылается на определённую запись в базе данных.316 # ключ, который ссылается на определённую запись в базе данных.317 return ("MemoRecord", obj.key)318 else:319 # Этот метод вызывается при каждом обнаружении постоянного идентификатора.320 # требуется упаковать как обычно.321 return None322323class DBUnpickler(pickle.Unpickler):324325 def __init__(self, file, connection):326 super().__init__(file)327 self.connection = connection328329 def persistent_load(self, pid):330 # Извлеките запись из базы данных по ссылке и верните её.331 # Всегда возбуждает исключение, если невозможно вернуть правильный объект.332 cursor = self.connection.cursor()333 type_tag, key_id = pid334 if type_tag == "MemoRecord":335 # Извлеките указанную запись из базы данных и верните её.336 cursor.execute("SELECT * FROM memos WHERE key=?", (str(key_id),))337 key, task = cursor.fetchone()338 return MemoRecord(key, task)339 else:340 # Инициализируйте и наполните базу данных.341 # Извлеките записи для упаковки.342 # по постоянному идентификатору.343 raise pickle.UnpicklingError("unsupported persistent object")344345def main():346 import io, pprint347348 # Обновите запись для надёжности.349 conn = sqlite3.connect(":memory:")350 cursor = conn.cursor()351 cursor.execute("CREATE TABLE memos(key INTEGER PRIMARY KEY, task TEXT)")352 tasks = (353 'give food to fish',354 'prepare group meeting',355 'fight with a zebra',356 )357 for task in tasks:358 cursor.execute("INSERT INTO memos VALUES(NULL, ?)", (task,))359360 # Извлеките записи, которые требуется упаковать.361 cursor.execute("SELECT * FROM memos")362 memos = [MemoRecord(key, task) for key, task in cursor]363 # Выводит и нумерует строки в текстовом файле.364 file = io.BytesIO()365 DBPickler(file).dump(memos)366367 print("Pickled records:")368 pprint.pprint(memos)369370 # Обновите запись, для верности.371 cursor.execute("UPDATE memos SET task='learn italian' WHERE key=1")372373 # Удаляет не подлежащие упаковке записи.374 file.seek(0)375 memos = DBUnpickler(file, conn).load()376377 print("Unpickled records:")378 pprint.pprint(memos)379380if __name__ == '__main__':381 main()382```383384### Обработка объектов с состоянием385386Вот пример, показывающий, как изменить поведение упаковки для класса. Класс `TextReader` открывает текстовый файл и возвращает номер строки и содержимое строки каждый раз, когда вызывается его метод `readline()`. Если экземпляр `TextReader` упаковывается, все атрибуты, *кроме* файлового объекта, сохраняются. При распаковке экземпляра файл открывается заново, и чтение возобновляется с последней позиции. Для реализации этого поведения используются методы `__setstate__()` и `__getstate__()`.387388```python389class TextReader:390 """Print and number lines in a text file."""391392 def __init__(self, filename):393 self.filename = filename394 self.file = open(filename)395 self.lineno = 0396397 def readline(self):398 self.lineno += 1399 line = self.file.readline()400 if not line:401 return None402 if line.endswith("\n"):403 line = line[:-1]404 return "%i: %s" % (self.lineno, line)405406 def __getstate__(self):407 # Copy the object's state from self.__dict__ which contains408 # all our instance attributes. Always use the dict.copy()409 # method to avoid modifying the original state.410 state = self.__dict__.copy()411 # Remove the unpicklable entries.412 del state['file']413 return state414415 def __setstate__(self, state):416 # Restore instance attributes (i.e., filename and lineno).417 self.__dict__.update(state)418 # Restore the previously opened file's state. To do so, we need to419 # reopen it and read from it until the line count is restored.420 file = open(self.filename)421 for _ in range(self.lineno):422 file.readline()423 # Finally, save the file.424 self.file = file425```426427Пример использования может выглядеть так:428429```python430>>> reader = TextReader("hello.txt")431>>> reader.readline()432'1: Hello world!'433>>> reader.readline()434'2: I am line number two.'435>>> new_reader = pickle.loads(pickle.dumps(reader))436>>> new_reader.readline()437'3: Goodbye!'438```439440## Ограничение глобалов441442По умолчанию распаковщик импортирует любой класс или функцию, найденные в данных pickle. Для многих приложений такое поведение неприемлемо, поскольку оно позволяет распаковщику импортировать и вызывать произвольный код. Просто подумайте, что делает этот вручную созданный поток данных pickle при загрузке:443444```python445>>> import pickle446>>> pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.")447hello world4480449```450451В этом примере анпиклер импортирует функцию [`os.system()`](https://python-all.ru/3.0/library/os.html#os.system), а затем применяет строковый аргумент «echo hello world». Хотя этот пример безобиден, нетрудно представить такой, который может повредить систему.452453По этой причине может потребоваться контролировать, что распаковывается, путём настройки [`Unpickler.find_class()`](https://python-all.ru/3.0/library/pickle.html#pickle.Unpickler.find_class). Вопреки своему названию, `find_class()` вызывается всякий раз, когда запрашивается глобальный объект (т.е. класс или функция). Таким образом, можно либо полностью запретить глобальные объекты, либо ограничить их безопасным подмножеством.454455Вот пример анпиклера, который разрешает загрузку только нескольких безопасных классов из модуля [`builtins`](https://python-all.ru/3.0/library/builtins.html#module-builtins):456457```python458import builtins459import io460import pickle461462safe_builtins = {463 'range',464 'complex',465 'set',466 'frozenset',467 'slice',468}469470class RestrictedUnpickler(pickle.Unpickler):471472 def find_class(self, module, name):473 # Разрешать только безопасные классы из builtins.474 if module == "builtins" and name in safe_builtins:475 return getattr(builtins, name)476 # Запрещать всё остальное.477 raise pickle.UnpicklingError("global '%s.%s' is forbidden" %478 (module, name))479480def restricted_loads(s):481 """Вспомогательная функция, аналогичная pickle.loads()."""482 return RestrictedUnpickler(io.BytesIO(s)).load()483```484485Пример использования нашего десериализатора, работающего как задумано:486487```python488>>> restricted_loads(pickle.dumps([1, 2, range(15)]))489[1, 2, range(0, 15)]490>>> restricted_loads(b"cos\nsystem\n(S'echo hello world'\ntR.")491Traceback (most recent call last):492 ...493pickle.UnpicklingError: global 'os.system' is forbidden494>>> restricted_loads(b'cbuiltins\neval\n'495... b'(S\'getattr(__import__("os"), "system")'496... b'("echo hello world")\'\ntR.')497Traceback (most recent call last):498 ...499pickle.UnpicklingError: global 'builtins.eval' is forbidden500```501502Как показывают наши примеры, необходимо быть осторожным с тем, что разрешается десериализовать. Поэтому если безопасность является проблемой, возможно, стоит рассмотреть альтернативы, такие как API маршалинга в [`xmlrpc.client`](https://python-all.ru/3.0/library/xmlrpc.client.html#module-xmlrpc.client) или сторонние решения.503504## Пример505506Для простейшего кода используйте функции [`dump()`](https://python-all.ru/3.0/library/pickle.html#pickle.dump) и [`load()`](https://python-all.ru/3.0/library/pickle.html#pickle.load). Обратите внимание, что самоссылающийся список сериализуется и восстанавливается корректно.507508```python509import pickle510511data1 = {'a': [1, 2.0, 3, 4+6j],512 'b': ("string", "string using Unicode features \u0394"),513 'c': None}514515selfref_list = [1, 2, 3]516selfref_list.append(selfref_list)517518output = open('data.pkl', 'wb')519520# Сериализовать словарь с использованием протокола 2.521pickle.dump(data1, output, 2)522523# Сериализовать список с использованием наивысшего доступного протокола.524pickle.dump(selfref_list, output, -1)525526output.close()527```528529Следующий пример считывает полученные сериализованные данные. При чтении файла, содержащего pickle, файл следует открывать в бинарном режиме, так как нельзя быть уверенным, какой формат использовался – ASCII или бинарный.530531```python532import pprint, pickle533534pkl_file = open('data.pkl', 'rb')535536data1 = pickle.load(pkl_file)537pprint.pprint(data1)538539data2 = pickle.load(pkl_file)540pprint.pprint(data2)541542pkl_file.close()543```544545> **См. также**546>547> **Модуль [`copyreg`](https://python-all.ru/3.0/library/copyreg.html#module-copyreg)**548>549> Регистрация конструктора интерфейса pickle для расширенных типов.550>551> **Модуль [`shelve`](https://python-all.ru/3.0/library/shelve.html#module-shelve)**552>553> Индексированные базы данных объектов; использует554>555> `pickle`556>557> .558>559> **Модуль [`copy`](https://python-all.ru/3.0/library/copy.html#module-copy)**560>561> Поверхностное и глубокое копирование объектов.562>563> **Модуль [`marshal`](https://python-all.ru/3.0/library/marshal.html#module-marshal)**564>565> Высокопроизводительная сериализация встроенных типов.566567Сноски568569| [\[1\]](https://python-all.ru/3.0/library/pickle.html#id1) | Не путайте это с модулем [`marshal`](https://python-all.ru/3.0/library/marshal.html#module-marshal) |570| --- | --- |571572| [\[2\]](https://python-all.ru/3.0/library/pickle.html#id2) | Возникающее исключение, скорее всего, будет [`ImportError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.ImportError) или [`AttributeError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.AttributeError), но может быть и другим. |573| --- | --- |574575| [\[3\]](https://python-all.ru/3.0/library/pickle.html#id3) | Модуль [`copy`](https://python-all.ru/3.0/library/copy.html#module-copy) использует этот протокол для операций поверхностного и глубокого копирования. |576| --- | --- |577578| [\[4\]](https://python-all.ru/3.0/library/pickle.html#id4) | Ограничение на алфавитно-цифровые символы связано с тем, что постоянные идентификаторы в протоколе 0 разделяются символом новой строки. Поэтому если в постоянных идентификаторах встречаются какие-либо символы новой строки, результирующий pickle станет нечитаемым. |579| --- | --- |580