> **Источник:** https://python-all.ru/2.7/library/pickle.html
>
> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.

---

# 11.1. [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) – сериализация объектов Python

Модуль [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) реализует фундаментальный, но мощный алгоритм для сериализации и десериализации структуры объектов Python. «Пиклирование» – это процесс, при котором иерархия объектов Python преобразуется в поток байтов, а «распиклирование» – обратная операция, при которой поток байтов преобразуется обратно в иерархию объектов. Пиклирование (и распиклирование) также известно как «сериализация», «маршалинг», [1](https://python-all.ru/2.7/library/pickle.html#id11) или «уплощение», однако, чтобы избежать путаницы, здесь используются термины «пиклирование» и «распиклирование».

Эта документация описывает как модуль [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle), так и модуль [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle).

> **Предупреждение**
>
> Модуль [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) не защищён от ошибочных или вредоносных данных. Никогда не десериализуйте данные, полученные из ненадёжного или непроверенного источника.

## 11.1.1. Взаимосвязь с другими модулями Python

У модуля [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) есть оптимизированный собрат, называемый модулем [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle). Как следует из названия, [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle) написан на C, поэтому он может быть до 1000 раз быстрее, чем [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle). Однако он не поддерживает наследование от классов [`Pickler()`](https://python-all.ru/2.7/library/pickle.html#pickle.Pickler) и [`Unpickler()`](https://python-all.ru/2.7/library/pickle.html#pickle.Unpickler), потому что в [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle) это функции, а не классы. Большинству приложений эта функциональность не нужна, и они могут выиграть от повышенной производительности [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle). В остальном интерфейсы двух модулей почти идентичны; общий интерфейс описан в этом руководстве, а различия отмечены там, где это необходимо. В дальнейшем мы используем термин «pickle» для совместного описания модулей [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) и [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle).

Потоки данных, производимые двумя модулями, гарантированно взаимозаменяемы.

В Python есть более примитивный модуль сериализации [`marshal`](https://python-all.ru/2.7/library/marshal.html#module-marshal), но в целом [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) всегда должен быть предпочтительным способом сериализации объектов Python. [`marshal`](https://python-all.ru/2.7/library/marshal.html#module-marshal) существует в первую очередь для поддержки файлов `.pyc` в Python.

Модуль [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) отличается от [`marshal`](https://python-all.ru/2.7/library/marshal.html#module-marshal) несколькими важными особенностями:

- Модуль [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) отслеживает уже сериализованные объекты, чтобы последующие ссылки на тот же объект не сериализовались повторно. [`marshal`](https://python-all.ru/2.7/library/marshal.html#module-marshal) этого не делает.

  This has implications both for recursive objects and object sharing. Recursive objects are objects that contain references to themselves. These are not handled by marshal, and in fact, attempting to marshal recursive objects will crash your Python interpreter. Object sharing happens when there are multiple references to the same object in different places in the object hierarchy being serialized. [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) stores such objects only once, and ensures that all other references point to the master copy. Shared objects remain shared, which can be very important for mutable objects.
- [`marshal`](https://python-all.ru/2.7/library/marshal.html#module-marshal) нельзя использовать для сериализации пользовательских классов и их экземпляров. [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) может прозрачно сохранять и восстанавливать экземпляры классов, однако определение класса должно быть импортируемо и находиться в том же модуле, что и при сохранении объекта.
- Формат сериализации [`marshal`](https://python-all.ru/2.7/library/marshal.html#module-marshal) не гарантирует переносимости между версиями Python. Поскольку его основная задача – поддержка файлов `.pyc`, разработчики Python оставляют за собой право изменять формат сериализации несовместимым с предыдущими версиями образом, если возникнет необходимость. Формат сериализации [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) гарантирует обратную совместимость между выпусками Python.

Обратите внимание, что сериализация – это более примитивное понятие, чем персистентность; хотя [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) читает и записывает файловые объекты, он не решает проблему именования персистентных объектов, ни (ещё более сложную) проблему конкурентного доступа к персистентным объектам. Модуль [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) может преобразовать сложный объект в поток байтов и может преобразовать поток байтов обратно в объект с той же внутренней структурой. Вероятно, самое очевидное, что можно сделать с этими потоками байтов – записать их в файл, но также можно отправить их по сети или сохранить в базе данных. Модуль [`shelve`](https://python-all.ru/2.7/library/shelve.html#module-shelve) предоставляет простой интерфейс для пиклирования и распиклирования объектов в файлах баз данных в стиле DBM.

## 11.1.2. Формат потока данных

Формат данных, используемый [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle), является специфичным для Python. Это даёт преимущество: нет ограничений, налагаемых внешними стандартами, такими как XDR (который не может представлять разделение указателей); однако это означает, что программы не на Python могут не иметь возможности восстановить пиклированные объекты Python.

По умолчанию формат данных [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) использует печатное ASCII-представление. Это немного объёмнее, чем двоичное представление. Большим преимуществом использования печатного ASCII (и некоторых других характеристик представления [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle)) является то, что для целей отладки или восстановления человек может прочитать пиклированный файл с помощью стандартного текстового редактора.

В настоящее время существует 3 различных протокола, которые можно использовать для пиклирования.

- Версия протокола 0 является оригинальным ASCII-протоколом и обратно совместима с предыдущими версиями Python.
- Версия протокола 1 – это старый двоичный формат, также совместимый с ранними версиями Python.
- Версия протокола 2 была представлена в Python 2.3. Она обеспечивает гораздо более эффективную упаковку (pickling) [классов нового стиля](https://python-all.ru/2.7/glossary.html#term-new-style-class).

Смотрите [**PEP 307**](https://python-all.ru/2.7/library/pickle.html) для получения дополнительной информации.

Если *протокол* не указан, используется протокол 0. Если *протокол* указан как отрицательное значение или [`HIGHEST_PROTOCOL`](https://python-all.ru/2.7/library/pickle.html#pickle.HIGHEST_PROTOCOL), будет использована самая высокая доступная версия протокола.

Изменено в версии 2.3: Добавлен параметр *протокол*.

Двоичный формат, который немного эффективнее, можно выбрать, указав *протокол* версии \>= 1.

## 11.1.3. Использование

Чтобы сериализовать иерархию объектов, сначала создаётся pickler, затем вызывается его метод [`dump()`](https://python-all.ru/2.7/library/pickle.html#pickle.dump). Чтобы десериализовать поток данных, сначала создаётся unpickler, затем вызывается его метод [`load()`](https://python-all.ru/2.7/library/pickle.html#pickle.load). Модуль [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) предоставляет следующую константу:

#### `pickle.HIGHEST_PROTOCOL`

Самая высокая доступная версия протокола. Это значение можно передать как аргумент *protocol*.

Новое в версии 2.3.

> **Примечание**
>
> Файлы pickle, созданные с протоколами \>= 1, следует всегда открывать в бинарном режиме. Для старого ASCII-протокола pickle 0 можно использовать как текстовый, так и бинарный режим, при условии соблюдения согласованности.
>
> Файл pickle, записанный с протоколом 0 в бинарном режиме, будет содержать одиночные символы перевода строки в качестве разделителей строк и поэтому будет выглядеть «странно» при просмотре в Блокноте или других редакторах, не поддерживающих этот формат.

Модуль [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) предоставляет следующие функции для упрощения процесса упаковки:

#### `pickle.dump(obj, file[, protocol])`

Записывает пиклированное представление объекта *obj* в открытый файловый объект *file*. Это эквивалентно `Pickler(file, protocol).dump(obj)`.

Если параметр *протокол* опущен, используется протокол 0. Если *протокол* указан как отрицательное значение или [`HIGHEST_PROTOCOL`](https://python-all.ru/2.7/library/pickle.html#pickle.HIGHEST_PROTOCOL), будет использована самая высокая версия протокола.

Изменено в версии 2.3: Добавлен параметр *протокол*.

*file* должен иметь метод `write()`, который принимает один строковый аргумент. Таким образом, это может быть файловый объект, открытый для записи, объект [`StringIO`](https://python-all.ru/2.7/library/stringio.html#module-StringIO) или любой другой пользовательский объект, соответствующий этому интерфейсу.

#### `pickle.load(file)`

Читает строку из открытого файлового объекта *file* и интерпретирует её как поток данных pickle, восстанавливая и возвращая исходную иерархию объектов. Это эквивалентно `Unpickler(file).load()`.

*file* должен иметь два метода: метод `read()`, принимающий целочисленный аргумент, и метод [`readline()`](https://python-all.ru/2.7/library/readline.html#module-readline), не требующий аргументов. Оба метода должны возвращать строку. Таким образом, *file* может быть файловым объектом, открытым для чтения, объектом [`StringIO`](https://python-all.ru/2.7/library/stringio.html#module-StringIO) или любым другим пользовательским объектом, соответствующим этому интерфейсу.

Эта функция автоматически определяет, был ли поток данных записан в двоичном режиме или нет.

#### `pickle.dumps(obj[, protocol])`

Возвращает пиклированное представление объекта в виде строки, вместо записи в файл.

Если параметр *протокол* опущен, используется протокол 0. Если *протокол* указан как отрицательное значение или [`HIGHEST_PROTOCOL`](https://python-all.ru/2.7/library/pickle.html#pickle.HIGHEST_PROTOCOL), будет использована самая высокая версия протокола.

Изменено в версии 2.3: Был добавлен параметр *протокол*.

#### `pickle.loads(string)`

Читает иерархию пиклированных объектов из строки. Символы в строке после представления пиклированного объекта игнорируются.

Модуль [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) также определяет три исключения:

#### `exception pickle.PickleError`

Общий базовый класс для других исключений, определённых ниже. Наследует от [`Exception`](https://python-all.ru/2.7/library/exceptions.html#exceptions.Exception).

#### `exception pickle.PicklingError`

Это исключение возбуждается, когда непиклируемый объект передаётся методу [`dump()`](https://python-all.ru/2.7/library/pickle.html#pickle.dump).

#### `exception pickle.UnpicklingError`

Это исключение возбуждается при возникновении проблемы распиклирования объекта. Обратите внимание, что во время распиклирования могут также возбуждаться другие исключения, включая (но не ограничиваясь) [`AttributeError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.AttributeError), [`EOFError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.EOFError), [`ImportError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ImportError) и [`IndexError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.IndexError).

Модуль [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) также экспортирует два вызываемых объекта: [2](https://python-all.ru/2.7/library/pickle.html#id12), [`Pickler`](https://python-all.ru/2.7/library/pickle.html#pickle.Pickler) и [`Unpickler`](https://python-all.ru/2.7/library/pickle.html#pickle.Unpickler).

#### `class pickle.Pickler(file[, protocol])`

Принимает файлоподобный объект, в который будет записан поток данных pickle.

Если параметр *protocol* опущен, используется протокол 0. Если *protocol* задан как отрицательное значение или [`HIGHEST_PROTOCOL`](https://python-all.ru/2.7/library/pickle.html#pickle.HIGHEST_PROTOCOL), будет использована самая высокая версия протокола.

Изменено в версии 2.3: Введён параметр *protocol*.

*file* должен иметь метод `write()`, принимающий один строковый аргумент. Таким образом, это может быть открытый файловый объект, объект [`StringIO`](https://python-all.ru/2.7/library/stringio.html#module-StringIO) или любой другой пользовательский объект, соответствующий этому интерфейсу.

Объекты [`Pickler`](https://python-all.ru/2.7/library/pickle.html#pickle.Pickler) определяют один (или два) открытых метода:

#### `dump(obj)`

Записывает представление *объекта* в pickle в открытый файловый объект, переданный конструктору. Будет использован бинарный или ASCII-формат в зависимости от значения аргумента *protocol*, переданного конструктору.

#### `clear_memo()`

Очищает «memo» (память) пиклера. Memo – это структура данных, которая запоминает, какие объекты пиклер уже видел, чтобы общие или рекурсивные объекты сериализовались по ссылке, а не по значению. Этот метод полезен при повторном использовании пиклеров.

> **Примечание**
>
> До Python 2.3 [`clear_memo()`](https://python-all.ru/2.7/library/pickle.html#pickle.Pickler.clear_memo) был доступен только для пиклеров, созданных [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle). В модуле [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) у пиклеров есть переменная экземпляра `memo`, представляющая собой словарь Python. Таким образом, чтобы очистить память для пиклера модуля [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle), можно было сделать следующее:
>
> ```python
> mypickler.memo.clear()
> ```
>
> Код, которому не требуется поддержка старых версий Python, должен просто использовать [`clear_memo()`](https://python-all.ru/2.7/library/pickle.html#pickle.Pickler.clear_memo).

Можно выполнить несколько вызовов метода [`dump()`](https://python-all.ru/2.7/library/pickle.html#pickle.dump) одного и того же экземпляра [`Pickler`](https://python-all.ru/2.7/library/pickle.html#pickle.Pickler). Эти вызовы должны затем быть сопоставлены с таким же количеством вызовов метода [`load()`](https://python-all.ru/2.7/library/pickle.html#pickle.load) соответствующего экземпляра [`Unpickler`](https://python-all.ru/2.7/library/pickle.html#pickle.Unpickler). Если один и тот же объект сериализуется несколькими вызовами [`dump()`](https://python-all.ru/2.7/library/pickle.html#pickle.dump), все [`load()`](https://python-all.ru/2.7/library/pickle.html#pickle.load) будут давать ссылки на один и тот же объект. [3](https://python-all.ru/2.7/library/pickle.html#id13)

Объекты [`Unpickler`](https://python-all.ru/2.7/library/pickle.html#pickle.Unpickler) определяются как:

#### `class pickle.Unpickler(file)`

Принимает файлоподобный объект, из которого будет читать поток данных pickle. Этот класс автоматически определяет, был ли поток данных записан в бинарном режиме или нет, поэтому ему не нужен флаг, как в фабрике [`Pickler`](https://python-all.ru/2.7/library/pickle.html#pickle.Pickler).

*file* должен иметь два метода: метод `read()`, принимающий целочисленный аргумент, и метод [`readline()`](https://python-all.ru/2.7/library/readline.html#module-readline), не требующий аргументов. Оба метода должны возвращать строку. Таким образом, *file* может быть файловым объектом, открытым для чтения, объектом [`StringIO`](https://python-all.ru/2.7/library/stringio.html#module-StringIO) или любым другим пользовательским объектом, соответствующим этому интерфейсу.

Объекты [`Unpickler`](https://python-all.ru/2.7/library/pickle.html#pickle.Unpickler) имеют один (или два) открытых метода:

#### `load()`

Читает представление объекта в pickle из открытого файлового объекта, переданного конструктору, и возвращает восстановленную иерархию объектов, указанную в нём.

Этот метод автоматически определяет, был ли поток данных записан в бинарном режиме или нет.

#### `noload()`

Этот метод похож на [`load()`](https://python-all.ru/2.7/library/pickle.html#pickle.load), за исключением того, что он не создаёт реально никаких объектов. Это полезно в первую очередь для поиска так называемых «persistent ids» (постоянных идентификаторов), которые могут быть использованы в потоке данных pickle. См. раздел [Протокол pickle](https://python-all.ru/2.7/library/pickle.html#pickle-protocol) ниже для получения дополнительных сведений.

**Примечание:** метод [`noload()`](https://python-all.ru/2.7/library/pickle.html#pickle.Unpickler.noload) в настоящее время доступен только для объектов [`Unpickler`](https://python-all.ru/2.7/library/pickle.html#pickle.Unpickler), созданных с помощью модуля [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle). Объекты [`Unpickler`](https://python-all.ru/2.7/library/pickle.html#pickle.Unpickler) модуля [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) не имеют метода [`noload()`](https://python-all.ru/2.7/library/pickle.html#pickle.Unpickler.noload).

## 11.1.4. Что можно упаковать и распаковать с помощью pickle?

Следующие типы можно сериализовать:

- `None`, `True` и `False`
- целые числа, длинные целые числа, числа с плавающей запятой, комплексные числа
- обычные и Unicode-строки
- кортежи, списки, множества и словари, содержащие только сериализуемые объекты
- Функции, определённые на верхнем уровне модуля.
- встроенные функции, определённые на верхнем уровне модуля
- классы, определённые на верхнем уровне модуля
- экземпляры таких классов, у которых [`__dict__`](https://python-all.ru/2.7/library/stdtypes.html#object.__dict__) или результат вызова [`__getstate__()`](https://python-all.ru/2.7/library/pickle.html#object.__getstate__) является сериализуемым (подробнее см. раздел [Протокол pickle](https://python-all.ru/2.7/library/pickle.html#pickle-protocol)).

Попытки сериализовать несериализуемые объекты вызовут исключение [`PicklingError`](https://python-all.ru/2.7/library/pickle.html#pickle.PicklingError); когда это происходит, некоторое количество байт может быть уже записано в базовый файл. Попытка сериализовать сильно рекурсивную структуру данных может превысить максимальную глубину рекурсии, в этом случае будет возбуждено исключение [`RuntimeError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.RuntimeError). Можно аккуратно увеличить этот предел с помощью [`sys.setrecursionlimit()`](https://python-all.ru/2.7/library/sys.html#sys.setrecursionlimit).

Обратите внимание, что функции (встроенные и определённые пользователем) упаковываются по «полностью квалифицированному» имени, а не по значению. Это означает, что упаковывается только имя функции вместе с именем модуля, в котором функция определена. Ни код функции, ни какие-либо её атрибуты не упаковываются. Поэтому определяющий модуль должен быть импортируемым в среде распаковки, и модуль должен содержать именованный объект, иначе будет возбуждено исключение. [4](https://python-all.ru/2.7/library/pickle.html#id14)

Аналогично, классы сериализуются по именованной ссылке, поэтому в среде десериализации действуют те же ограничения. Обратите внимание, что ни код класса, ни его данные не сериализуются, поэтому в следующем примере атрибут класса `attr` не восстанавливается в среде десериализации:

```python
class Foo:
    attr = 'a class attr'

picklestring = pickle.dumps(Foo)
```

Эти ограничения объясняют, почему сериализуемые функции и классы должны быть определены на верхнем уровне модуля.

Аналогично, при сериализации экземпляров классов код и данные самого класса не сериализуются вместе с ними. Сериализуются только данные экземпляра. Это сделано намеренно, чтобы можно было исправить ошибки в классе или добавить в него методы и по-прежнему загружать объекты, созданные с помощью более старой версии класса. Если планируется использовать долгоживущие объекты, которые переживут множество версий класса, будет разумно добавить в объекты номер версии, чтобы можно было выполнять соответствующие преобразования с помощью метода [`__setstate__()`](https://python-all.ru/2.7/library/pickle.html#object.__setstate__) класса.

## 11.1.5. Протокол pickle

В этом разделе описывается «протокол pickle», который определяет интерфейс между пиклером/анпиклером и сериализуемыми объектами. Этот протокол предоставляет стандартный способ определения, настройки и управления тем, как объекты сериализуются и десериализуются. Описание в этом разделе не охватывает специальные настройки, которые можно применить для обеспечения большей безопасности среды распаковки от недоверенных потоков данных pickle; см. раздел [Создание подклассов Unpickler](https://python-all.ru/2.7/library/pickle.html#pickle-sub) для получения более подробной информации.

### 11.1.5.1. Упаковка и распаковка обычных экземпляров классов

#### `object.__getinitargs__()`

Когда сериализованный экземпляр класса распаковывается, его метод [`__init__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__init__) обычно *не* вызывается. Если необходимо, чтобы метод [`__init__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__init__) вызывался при распаковке, класс старого стиля может определить метод [`__getinitargs__()`](https://python-all.ru/2.7/library/pickle.html#object.__getinitargs__), который должен возвращать *кортеж* позиционных аргументов, передаваемых конструктору класса (например, [`__init__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__init__)). Именованные аргументы не поддерживаются. Метод [`__getinitargs__()`](https://python-all.ru/2.7/library/pickle.html#object.__getinitargs__) вызывается во время упаковки; возвращаемый кортеж включается в pickle для экземпляра.

#### `object.__getnewargs__()`

Типы нового стиля могут предоставлять метод [`__getnewargs__()`](https://python-all.ru/2.7/library/pickle.html#object.__getnewargs__), который используется для протокола 2. Реализация этого метода необходима, если тип устанавливает некоторые внутренние инварианты при создании экземпляра, или если выделение памяти зависит от значений, передаваемых методу [`__new__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__new__) для этого типа (как для кортежей и строк). Экземпляры [класса нового стиля](https://python-all.ru/2.7/glossary.html#term-new-style-class) `C` создаются с помощью

```python
obj = C.__new__(C, *args)
```

где *аргументы* – это результат вызова [`__getnewargs__()`](https://python-all.ru/2.7/library/pickle.html#object.__getnewargs__) для исходного объекта; если [`__getnewargs__()`](https://python-all.ru/2.7/library/pickle.html#object.__getnewargs__) отсутствует, предполагается пустой кортеж.

#### `object.__getstate__()`

Классы могут дополнительно влиять на то, как упаковываются их экземпляры; если класс определяет метод [`__getstate__()`](https://python-all.ru/2.7/library/pickle.html#object.__getstate__), он вызывается и возвращаемое состояние упаковывается как содержимое экземпляра, а не содержимое его словаря. Если метод [`__getstate__()`](https://python-all.ru/2.7/library/pickle.html#object.__getstate__) отсутствует, упаковывается [`__dict__`](https://python-all.ru/2.7/library/stdtypes.html#object.__dict__) экземпляра.

#### `object.__setstate__(state)`

При распаковке, если класс также определяет метод [`__setstate__()`](https://python-all.ru/2.7/library/pickle.html#object.__setstate__), он вызывается с распакованным состоянием. [5](https://python-all.ru/2.7/library/pickle.html#id15) Если метода [`__setstate__()`](https://python-all.ru/2.7/library/pickle.html#object.__setstate__) нет, упакованное состояние должно быть словарём, и его элементы присваиваются словарю нового экземпляра. Если класс определяет оба метода [`__getstate__()`](https://python-all.ru/2.7/library/pickle.html#object.__getstate__) и [`__setstate__()`](https://python-all.ru/2.7/library/pickle.html#object.__setstate__), объект состояния не обязан быть словарём, и эти методы могут делать что угодно. [6](https://python-all.ru/2.7/library/pickle.html#id16)

> **Примечание**
>
> Для [классов нового стиля](https://python-all.ru/2.7/glossary.html#term-new-style-class), если [`__getstate__()`](https://python-all.ru/2.7/library/pickle.html#object.__getstate__) возвращает ложное значение, метод [`__setstate__()`](https://python-all.ru/2.7/library/pickle.html#object.__setstate__) не будет вызван.

> **Примечание**
>
> При распаковке для экземпляра могут вызываться некоторые методы, такие как [`__getattr__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__getattr__), [`__getattribute__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__getattribute__) или [`__setattr__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__setattr__). Если эти методы зависят от выполнения некоторого внутреннего инварианта, тип должен реализовать либо [`__getinitargs__()`](https://python-all.ru/2.7/library/pickle.html#object.__getinitargs__), либо [`__getnewargs__()`](https://python-all.ru/2.7/library/pickle.html#object.__getnewargs__), чтобы установить такой инвариант; в противном случае не будут вызваны ни [`__new__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__new__), ни [`__init__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__init__).

### 11.1.5.2. Упаковка и распаковка типов расширений

#### `object.__reduce__()`

Когда `Pickler` встречает объект типа, о котором ничего не знает (например, тип расширения), он ищет в двух местах подсказку, как его упаковать. Один вариант – чтобы объект реализовал метод [`__reduce__()`](https://python-all.ru/2.7/library/pickle.html#object.__reduce__). Если он предоставлен, при упаковке [`__reduce__()`](https://python-all.ru/2.7/library/pickle.html#object.__reduce__) будет вызван без аргументов и должен вернуть либо строку, либо кортеж.

Если возвращается строка, она задаёт имя глобальной переменной, содержимое которой упаковывается обычным образом. Строка, возвращаемая [`__reduce__()`](https://python-all.ru/2.7/library/pickle.html#object.__reduce__), должна быть локальным именем объекта относительно его модуля; модуль pickle ищет пространство имён модуля, чтобы определить модуль объекта.

Если возвращается кортеж, он должен содержать от двух до пяти элементов. Необязательные элементы можно опустить или передать `None` в качестве их значения. Содержимое этого кортежа упаковывается как обычно и используется для восстановления объекта при распаковке. Семантика каждого элемента:

- Вызываемый объект, который будет вызван для создания начальной версии объекта. Следующий элемент кортежа предоставит аргументы для этого вызываемого объекта, а последующие элементы – дополнительную информацию о состоянии, которая затем будет использована для полного восстановления упакованных данных.

  В среде распаковки этот объект должен быть классом, вызываемым объектом, зарегистрированным как «безопасный конструктор» (см. ниже), или иметь атрибут `__safe_for_unpickling__` с истинным значением. В противном случае в среде распаковки будет возбуждено `UnpicklingError`. Обратите внимание, что, как обычно, вызываемый объект упаковывается по имени.
- Кортеж аргументов для вызываемого объекта.

  Изменено в версии 2.5: Ранее этот аргумент мог также быть `None`.
- Необязательно, состояние объекта, которое будет передано методу [`__setstate__()`](https://python-all.ru/2.7/library/pickle.html#object.__setstate__) объекта, как описано в разделе [Упаковка и распаковка обычных экземпляров классов](https://python-all.ru/2.7/library/pickle.html#pickle-inst). Если у объекта нет метода [`__setstate__()`](https://python-all.ru/2.7/library/pickle.html#object.__setstate__), то, как и выше, значение должно быть словарём, и оно будет добавлено в [`__dict__`](https://python-all.ru/2.7/library/stdtypes.html#object.__dict__) объекта.
- Необязательно, итератор (не последовательность), выдающий последовательные элементы списка. Эти элементы списка будут упакованы и добавлены к объекту с помощью `obj.append(item)` или `obj.extend(list_of_items)`. Это в основном используется для подклассов списка, но может использоваться и другими классами, если они имеют методы `append()` и `extend()` с соответствующей сигнатурой. (Используется ли `append()` или `extend()`, зависит от используемой версии протокола pickle, а также от количества добавляемых элементов, поэтому должны поддерживаться оба.)
- Необязательно, итератор (не последовательность), выдающий последовательные элементы словаря, которые должны быть кортежами вида `(key, value)`. Эти элементы будут упакованы и сохранены в объект с помощью `obj[key] = value`. Это в основном используется для подклассов словаря, но может использоваться и другими классами, если они реализуют [`__setitem__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__setitem__).

#### `object.__reduce_ex__(protocol)`

Иногда полезно знать версию протокола при реализации [`__reduce__()`](https://python-all.ru/2.7/library/pickle.html#object.__reduce__). Это можно сделать, реализовав метод с именем [`__reduce_ex__()`](https://python-all.ru/2.7/library/pickle.html#object.__reduce_ex__) вместо [`__reduce__()`](https://python-all.ru/2.7/library/pickle.html#object.__reduce__). [`__reduce_ex__()`](https://python-all.ru/2.7/library/pickle.html#object.__reduce_ex__), если он существует, вызывается в предпочтение [`__reduce__()`](https://python-all.ru/2.7/library/pickle.html#object.__reduce__) (вы всё ещё можете предоставить [`__reduce__()`](https://python-all.ru/2.7/library/pickle.html#object.__reduce__) для обратной совместимости). Метод [`__reduce_ex__()`](https://python-all.ru/2.7/library/pickle.html#object.__reduce_ex__) будет вызван с одним целочисленным аргументом – версией протокола.

Класс [`object`](https://python-all.ru/2.7/library/functions.html#object) реализует как [`__reduce__()`](https://python-all.ru/2.7/library/pickle.html#object.__reduce__), так и [`__reduce_ex__()`](https://python-all.ru/2.7/library/pickle.html#object.__reduce_ex__); однако, если подкласс переопределяет [`__reduce__()`](https://python-all.ru/2.7/library/pickle.html#object.__reduce__), но не [`__reduce_ex__()`](https://python-all.ru/2.7/library/pickle.html#object.__reduce_ex__), реализация [`__reduce_ex__()`](https://python-all.ru/2.7/library/pickle.html#object.__reduce_ex__) обнаруживает это и вызывает [`__reduce__()`](https://python-all.ru/2.7/library/pickle.html#object.__reduce__).

Альтернатива реализации метода [`__reduce__()`](https://python-all.ru/2.7/library/pickle.html#object.__reduce__) на упаковываемом объекте – зарегистрировать вызываемый объект в модуле [`copy_reg`](https://python-all.ru/2.7/library/copy_reg.html#module-copy_reg). Этот модуль предоставляет способ регистрации «функций сокращения» (reduction functions) и конструкторов для пользовательских типов. Функции сокращения имеют ту же семантику и интерфейс, что и метод [`__reduce__()`](https://python-all.ru/2.7/library/pickle.html#object.__reduce__), описанный выше, за исключением того, что они вызываются с одним аргументом – упаковываемым объектом.

Зарегистрированный конструктор считается «безопасным конструктором» для целей распаковки, как описано выше.

### 11.1.5.3. Упаковка и распаковка внешних объектов

В интересах сохранения объектов (object persistence) модуль [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) поддерживает понятие ссылки на объект вне потока упакованных данных. Такие объекты обозначаются «постоянным идентификатором» (persistent id), который представляет собой произвольную строку из печатных ASCII-символов. Разрешение таких имён не определяется модулем [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle); он делегирует это разрешение пользовательским функциям упаковщика и распаковщика. [7](https://python-all.ru/2.7/library/pickle.html#id17)

Чтобы определить разрешение внешних постоянных идентификаторов, необходимо установить атрибут `persistent_id` объекта pickler и атрибут `persistent_load` объекта unpickler.

Чтобы упаковать объекты, имеющие внешний постоянный идентификатор, упаковщик должен иметь пользовательский метод `persistent_id()`, который принимает объект в качестве аргумента и возвращает либо `None`, либо постоянный идентификатор для этого объекта. Когда возвращается `None`, упаковщик просто упаковывает объект как обычно. Когда возвращается строка постоянного идентификатора, упаковщик упаковывает эту строку вместе с маркером, чтобы распаковщик распознал строку как постоянный идентификатор.

Чтобы распаковать внешние объекты, распаковщик должен иметь пользовательскую функцию `persistent_load()`, которая принимает строку постоянного идентификатора и возвращает ссылочный объект.

Вот глупый пример, который *возможно* прояснит ситуацию:

```python
import pickle
from cStringIO import StringIO

src = StringIO()
p = pickle.Pickler(src)

def persistent_id(obj):
    if hasattr(obj, 'x'):
        return 'the value %d' % obj.x
    else:
        return None

p.persistent_id = persistent_id

class Integer:
    def __init__(self, x):
        self.x = x
    def __str__(self):
        return 'My name is integer %d' % self.x

i = Integer(7)
print i
p.dump(i)

datastream = src.getvalue()
print repr(datastream)
dst = StringIO(datastream)

up = pickle.Unpickler(dst)

class FancyInteger(Integer):
    def __str__(self):
        return 'I am the integer %d' % self.x

def persistent_load(persid):
    if persid.startswith('the value '):
        value = int(persid.split()[2])
        return FancyInteger(value)
    else:
        raise pickle.UnpicklingError, 'Invalid persistent id'

up.persistent_load = persistent_load

j = up.load()
print j
```

В модуле [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle) атрибут `persistent_load` распаковщика также может быть установлен в список Python; в этом случае, когда распаковщик достигает постоянного идентификатора, строка постоянного идентификатора просто добавляется в этот список. Эта функциональность существует для того, чтобы поток данных pickle можно было «пронюхать» на предмет ссылок на объекты без фактического создания всех объектов в pickle. [8](https://python-all.ru/2.7/library/pickle.html#id18) Установка `persistent_load` в список обычно используется в сочетании с методом `noload()` на Unpickler.

## 11.1.6. Создание подклассов Распаковщика

По умолчанию распаковка импортирует любой класс, который найдет в данных pickle. Вы можете точно контролировать, что будет распаковано и что будет вызвано, настроив свой распаковщик. К сожалению, способ это сделать различается в зависимости от того, используете ли вы [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) или [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle). [9](https://python-all.ru/2.7/library/pickle.html#id19)

В модуле [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) вам нужно создать подкласс от `Unpickler`, переопределив метод `load_global()`. `load_global()` должен читать две строки из потока данных pickle, где первая строка будет именем модуля, содержащего класс, а вторая – именем класса экземпляра. Затем он ищет класс, возможно импортируя модуль и извлекая атрибут, после чего добавляет найденное в стек распаковщика. Позже этот класс будет присвоен атрибуту `__class__` пустого класса, что является способом магического создания экземпляра без вызова [`__init__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__init__) его класса. Ваша задача (если вы решите её принять) – сделать так, чтобы `load_global()` помещал в стек распаковщика известную безопасную версию любого класса, который вы считаете безопасным для распаковки. Создать такой класс – ваша забота. Или вы можете возбудить ошибку, если хотите запретить любую распаковку экземпляров. Если это звучит как хак, вы правы. Обратитесь к исходному коду, чтобы заставить это работать.

С [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle) дела обстоят немного чище, но ненамного. Чтобы контролировать, что будет распаковано, вы можете установить атрибут `find_global` распаковщика в функцию или `None`. Если это `None`, то любые попытки распаковать экземпляры возбудят `UnpicklingError`. Если это функция, то она должна принимать имя модуля и имя класса и возвращать соответствующий объект класса. Она отвечает за поиск класса и выполнение необходимых импортов, и может возбудить ошибку, чтобы предотвратить распаковку экземпляров этого класса.

Мораль этой истории в том, что нужно быть очень осторожным с источником строк, которые ваше приложение распаковывает.

## 11.1.7. Пример

Для простейшего кода используйте функции `dump()` и `load()`. Обратите внимание, что самоссылающийся список корректно упаковывается и восстанавливается.

```python
import pickle

data1 = {'a': [1, 2.0, 3, 4+6j],
         'b': ('string', u'Unicode string'),
         'c': None}

selfref_list = [1, 2, 3]
selfref_list.append(selfref_list)

output = open('data.pkl', 'wb')

# Сериализовать словарь с помощью протокола 0.
pickle.dump(data1, output)

# Сериализовать список с использованием наивысшего доступного протокола.
pickle.dump(selfref_list, output, -1)

output.close()
```

Следующий пример считывает полученные сериализованные данные. При чтении файла, содержащего pickle, файл следует открывать в бинарном режиме, так как нельзя быть уверенным, какой формат использовался – ASCII или бинарный.

```python
import pprint, pickle

pkl_file = open('data.pkl', 'rb')

data1 = pickle.load(pkl_file)
pprint.pprint(data1)

data2 = pickle.load(pkl_file)
pprint.pprint(data2)

pkl_file.close()
```

Вот более крупный пример, показывающий, как изменить поведение упаковки для класса. Класс `TextReader` открывает текстовый файл и возвращает номер строки и содержимое строки каждый раз при вызове метода `readline()`. Если экземпляр `TextReader` упаковывается, сохраняются все атрибуты *кроме* файлового объекта. При распаковке экземпляра файл открывается заново и чтение продолжается с последней позиции. Методы [`__setstate__()`](https://python-all.ru/2.7/library/pickle.html#object.__setstate__) и [`__getstate__()`](https://python-all.ru/2.7/library/pickle.html#object.__getstate__) используются для реализации такого поведения.

```python
#!/usr/local/bin/python

class TextReader:
    """Вывести и пронумеровать строки в текстовом файле."""
    def __init__(self, file):
        self.file = file
        self.fh = open(file)
        self.lineno = 0

    def readline(self):
        self.lineno = self.lineno + 1
        line = self.fh.readline()
        if not line:
            return None
        if line.endswith("\n"):
            line = line[:-1]
        return "%d: %s" % (self.lineno, line)

    def __getstate__(self):
        odict = self.__dict__.copy() # Скопировать словарь, так как он изменяется
        del odict['fh']              # Удалить запись дескриптора файла
        return odict

    def __setstate__(self, dict):
        fh = open(dict['file'])      # Переоткрыть файл
        count = dict['lineno']       # Читать из файла...
        while count:                 # Пока счётчик строк не восстановится
            fh.readline()
            count = count - 1
        self.__dict__.update(dict)   # Обновить атрибуты
        self.fh = fh                 # Сохранить файловый объект
```

Пример использования может выглядеть так:

```python
>>> import TextReader
>>> obj = TextReader.TextReader("TextReader.py")
>>> obj.readline()
'1: #!/usr/local/bin/python'
>>> obj.readline()
'2: '
>>> obj.readline()
'3: class TextReader:'
>>> import pickle
>>> pickle.dump(obj, open('save.p', 'wb'))
```

Чтобы убедиться, что [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) работает между процессами Python, запустите другой сеанс Python перед продолжением. Далее описанное может происходить как в том же процессе, так и в новом.

```python
>>> import pickle
>>> reader = pickle.load(open('save.p', 'rb'))
>>> reader.readline()
'4:     """Print and number lines in a text file."""'
```

> **См. также**
>
> **Модуль [`copy_reg`](https://python-all.ru/2.7/library/copy_reg.html#module-copy_reg)**
>
> Регистрация конструктора интерфейса pickle для расширенных типов.
>
> **Модуль [`shelve`](https://python-all.ru/2.7/library/shelve.html#module-shelve)**
>
> Индексированные базы данных объектов; использует [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle).
>
> **Модуль [`copy`](https://python-all.ru/2.7/library/copy.html#module-copy)**
>
> Поверхностное и глубокое копирование объектов.
>
> **Модуль [`marshal`](https://python-all.ru/2.7/library/marshal.html#module-marshal)**
>
> Высокопроизводительная сериализация встроенных типов.

# 11.2. [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle) – более быстрый [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle)

Модуль [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle) поддерживает сериализацию и десериализацию объектов Python, предоставляя интерфейс и функциональность, почти идентичные модулю [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle). Есть несколько различий, самое важное – производительность и возможность создания подклассов.

Во-первых, [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle) может быть до 1000 раз быстрее [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle), поскольку первый реализован на C. Во-вторых, в модуле [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle) вызываемые объекты `Pickler()` и `Unpickler()` являются функциями, а не классами. Это означает, что их нельзя использовать для создания пользовательских подклассов упаковки и распаковки. Большинству приложений эта функциональность не нужна, и они могут воспользоваться значительно улучшенной производительностью модуля [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle).

Поток данных pickle, создаваемый [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) и [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle), идентичен, поэтому можно использовать [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) и [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle) взаимозаменяемо с существующими pickles. [10](https://python-all.ru/2.7/library/pickle.html#id20)

Существуют дополнительные небольшие различия в API между [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle) и [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle), однако для большинства приложений они взаимозаменяемы. Более подробная документация приведена в документации модуля [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle), которая включает список задокументированных различий.

Сноски

**[1](https://python-all.ru/2.7/library/pickle.html#id1)**

Не путайте это с модулем [`marshal`](https://python-all.ru/2.7/library/marshal.html#module-marshal)

**[2](https://python-all.ru/2.7/library/pickle.html#id2)**

В модуле [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) эти вызываемые объекты являются классами, которые можно наследовать для настройки поведения. Однако в модуле [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle) эти вызываемые объекты являются фабричными функциями и поэтому не могут быть унаследованы. Одна из распространённых причин создания подкласса – контроль того, какие объекты можно распаковать. Подробнее см. раздел [Наследование распаковщиков](https://python-all.ru/2.7/library/pickle.html#pickle-sub).

**[3](https://python-all.ru/2.7/library/pickle.html#id3)**

*Предупреждение*: это предназначено для упаковки нескольких объектов без промежуточных изменений объектов или их частей. Если изменить объект, а затем упаковать его снова, используя тот же экземпляр `Pickler`, объект не упаковывается заново – упаковывается ссылка на него, и `Unpickler` вернёт старое значение, а не изменённое. Здесь есть две проблемы: (1) обнаружение изменений и (2) маршалинг минимального набора изменений. Сборка мусора также может стать проблемой.

**[4](https://python-all.ru/2.7/library/pickle.html#id4)**

Возникающее исключение, скорее всего, будет [`ImportError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ImportError) или [`AttributeError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.AttributeError), но может быть и другим.

**[5](https://python-all.ru/2.7/library/pickle.html#id5)**

Эти методы также можно использовать для реализации копирования экземпляров классов.

**[6](https://python-all.ru/2.7/library/pickle.html#id6)**

Этот протокол также используется операциями поверхностного и глубокого копирования, определёнными в модуле [`copy`](https://python-all.ru/2.7/library/copy.html#module-copy).

**[7](https://python-all.ru/2.7/library/pickle.html#id7)**

Реальный механизм связывания этих пользовательских функций немного отличается для [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) и [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle). Приведённое здесь описание работает одинаково для обеих реализаций. Пользователи модуля [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle) также могут использовать наследование для достижения тех же результатов, переопределяя методы `persistent_id()` и `persistent_load()` в производных классах.

**[8](https://python-all.ru/2.7/library/pickle.html#id8)**

Оставим вас с образом Гвидо и Джима, сидящих и нюхающих пикули в своих гостиных.

**[9](https://python-all.ru/2.7/library/pickle.html#id9)**

Предостережение: описанные здесь механизмы используют внутренние атрибуты и методы, которые могут измениться в будущих версиях Python. Мы намерены когда-нибудь предоставить общий интерфейс для управления этим поведением, который будет работать как с [`pickle`](https://python-all.ru/2.7/library/pickle.html#module-pickle), так и с [`cPickle`](https://python-all.ru/2.7/library/pickle.html#module-cPickle).

**[10](https://python-all.ru/2.7/library/pickle.html#id10)**

Поскольку формат данных pickle на самом деле является крошечным стековым языком программирования и допускает некоторую свободу в кодировании определённых объектов, возможно, что два модуля создают разные потоки данных для одних и тех же входных объектов. Однако гарантируется, что они всегда смогут читать потоки данных друг друга.
