pickle — сериализация объектов Python
Исходный код: Lib/pickle.py
Модуль pickle реализует двоичные протоколы для сериализации и десериализации структур объектов Python. «Пиклинг» — это процесс преобразования иерархии объектов Python в поток байтов, а «распиклинг» — обратная операция, при которой поток байтов (из двоичного файла или объекта, подобного bytes) преобразуется обратно в иерархию объектов. Пиклинг (и распиклинг) также называют «сериализацией», «маршалингом» [1] или «уплощением»; однако, чтобы избежать путаницы, здесь используются термины «пиклинг» и «распиклинг».
Предупреждение
Модуль pickle не является безопасным. Распаковывайте только те данные, которым доверяете.
Можно создать вредоносные данные pickle, которые выполнят произвольный код при распиклинге. Никогда не распаковывайте данные, которые могли поступить из ненадёжного источника или быть изменены.
Если необходимо убедиться, что данные не были изменены, рассмотрите возможность их подписания с помощью hmac.
Для обработки недоверенных данных могут быть более уместны безопасные форматы сериализации, например json. См. Сравнение с json.
Связь с другими модулями Python
Сравнение с marshal
В Python есть более примитивный модуль сериализации под названием marshal, но в целом для сериализации объектов Python всегда предпочтительно использовать pickle. Модуль marshal существует главным образом для поддержки файлов .pyc.
Модуль pickle отличается от marshal по нескольким важным аспектам:
-
Модуль
pickleотслеживает объекты, которые уже были сериализованы, поэтому последующие ссылки на тот же объект не сериализуются повторно.marshalэтого не делает.Это имеет значение как для рекурсивных объектов, так и для совместного использования объектов. Рекурсивные объекты — это объекты, содержащие ссылки на самих себя. marshal не обрабатывает такие объекты; более того, попытка выполнить маршалинг рекурсивных объектов приведёт к аварийному завершению интерпретатора Python. Совместное использование объектов возникает, когда на один и тот же объект в разных местах сериализуемой иерархии объектов ссылаются несколько раз.
pickleсохраняет такие объекты только один раз и гарантирует, что все остальные ссылки указывают на основную копию. Совместно используемые объекты остаются общими, что может быть очень важно для изменяемых объектов. -
Модуль
marshalнельзя использовать для сериализации классов, определённых пользователем, и их экземпляров.pickleможет прозрачно сохранять и восстанавливать экземпляры классов, однако определение класса должно быть импортируемым и находиться в том же модуле, что и при сохранении объекта. - Формат сериализации
marshalне гарантированно переносим между версиями Python. Поскольку его основное назначение — поддержка файлов.pyc, разработчики Python оставляют за собой право при необходимости менять формат сериализации способами, нарушающими обратную совместимость. Для формата сериализацииpickleгарантируется обратная совместимость между выпусками Python при условии, что выбран совместимый протокол pickle, а код пиклинга и распиклинга учитывает различия типов между Python 2 и Python 3, если ваши данные пересекают эту границу несовместимых изменений языка.
Сравнение с json
Между протоколами pickle и JSON (обозначение объектов JavaScript) есть принципиальные различия:
- JSON — это текстовый формат сериализации (он выдаёт текст Unicode, хотя в большинстве случаев затем этот текст кодируется в
utf-8), тогда как pickle — двоичный формат сериализации; - JSON понятен человеку, а pickle — нет;
- JSON совместим с другими системами и широко используется за пределами экосистемы Python, тогда как pickle специфичен для Python;
- JSON по умолчанию может представлять лишь подмножество встроенных типов Python и не поддерживает пользовательские классы; pickle может представлять чрезвычайно большое количество типов Python (многие из них автоматически благодаря умелому использованию средств интроспекции Python; сложные случаи можно обработать, реализовав специальные API объектов);
- В отличие от pickle, десериализация недоверенного JSON сама по себе не создаёт уязвимости, позволяющей выполнять произвольный код.
См. также
Модуль json: модуль стандартной библиотеки, позволяющий выполнять сериализацию и десериализацию JSON.
Формат потока данных
Формат данных, используемый модулем pickle, специфичен для Python. Это даёт преимущество: отсутствуют ограничения внешних стандартов, таких как JSON (который не может представлять совместное использование указателей); однако из-за этого программы, не написанные на Python, могут не уметь восстанавливать объекты Python из данных pickle.
По умолчанию формат данных pickle использует относительно компактное двоичное представление. Если вам требуется оптимальный размер, данные pickle можно эффективно сжать.
Модуль pickletools содержит инструменты для анализа потоков данных, созданных модулем pickle. Исходный код pickletools содержит подробные комментарии об операциях, используемых протоколами pickle.
В настоящее время для пиклинга можно использовать 6 различных протоколов. Чем выше версия протокола, тем более новая версия Python нужна для чтения созданных данных pickle.
- Протокол версии 0 — исходный протокол, «понятный человеку»; он обратно совместим с более ранними версиями Python.
- Протокол версии 1 — старый двоичный формат, также совместимый с более ранними версиями Python.
- Протокол версии 2 появился в Python 2.3. Он обеспечивает гораздо более эффективный пиклинг классов нового стиля. Сведения об улучшениях, внесённых протоколом 2, см. в PEP 307.
- Протокол версии 3 появился в Python 3.0. Он явно поддерживает объекты
bytes, и данные, созданные с его помощью, нельзя распиклить в Python 2.x. Это был протокол по умолчанию в Python 3.0–3.7. - Протокол версии 4 появился в Python 3.4. Он добавляет поддержку очень больших объектов, пиклинга большего числа видов объектов и некоторые оптимизации формата данных. Начиная с Python 3.8 это протокол по умолчанию. Сведения об улучшениях, внесённых протоколом 4, см. в PEP 3154.
- Протокол версии 5 появился в Python 3.8. Он добавляет поддержку данных вне полосы и ускоряет обработку данных внутри полосы. Сведения об улучшениях, внесённых протоколом 5, см. в PEP 574.
Примечание
Сериализация — понятие более примитивное, чем сохранение состояния; хотя pickle читает и записывает файловые объекты, он не решает задачу именования сохраняемых объектов и тем более более сложную задачу параллельного доступа к сохраняемым объектам. Модуль pickle может преобразовать сложный объект в поток байтов, а затем преобразовать этот поток в объект с той же внутренней структурой. Самое очевидное применение таких потоков байтов — запись в файл, но их также можно передавать по сети или хранить в базе данных. Модуль shelve предоставляет простой интерфейс для пиклинга и распиклинга объектов в файлах баз данных в стиле DBM.
Интерфейс модуля
Чтобы сериализовать иерархию объектов, просто вызовите функцию dumps(). Аналогично, чтобы десериализовать поток данных, вызовите функцию loads(). Однако, если вам требуется больше контроля над сериализацией и десериализацией, можно создать объект Pickler или Unpickler соответственно.
Модуль pickle предоставляет следующие константы:
-
pickle.HIGHEST_PROTOCOL -
Целое число — наибольшая доступная версия протокола. Это значение можно передать в качестве значения protocol функциям
dump()иdumps(), а также конструкторуPickler.
-
pickle.DEFAULT_PROTOCOL -
Целое число — версия протокола, используемая по умолчанию при сериализации с помощью pickle. Может быть меньше
HIGHEST_PROTOCOL. В настоящее время по умолчанию используется протокол 4, впервые появившийся в Python 3.4 и несовместимый с предыдущими версиями.Изменено в версии 3.0: По умолчанию используется протокол 3.
Изменено в версии 3.8: По умолчанию используется протокол 4.
Модуль pickle предоставляет следующие функции, упрощающие процесс сериализации с помощью pickle:
-
pickle.dump(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None) -
Записать сериализованное представление объекта obj в открытый файловый объект file. Это эквивалентно
Pickler(file, protocol).dump(obj).Аргументы file, protocol, fix_imports и buffer_callback имеют то же значение, что и в конструкторе
Pickler.Изменено в версии 3.8: Добавлен аргумент buffer_callback.
-
pickle.dumps(obj, protocol=None, *, fix_imports=True, buffer_callback=None) -
Вернуть сериализованное представление объекта obj в виде объекта
bytes, а не записывать его в файл.Аргументы protocol, fix_imports и buffer_callback имеют то же значение, что и в конструкторе
Pickler.Изменено в версии 3.8: Добавлен аргумент buffer_callback.
-
pickle.load(file, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None) -
Прочитать сериализованное представление объекта из открытого файлового объекта file и вернуть восстановленную иерархию объектов, заданную в нём. Это эквивалентно
Unpickler(file).load().Версия протокола pickle определяется автоматически, поэтому указывать аргумент протокола не требуется. Байты после сериализованного представления объекта игнорируются.
Аргументы file, fix_imports, encoding, errors, strict и buffers имеют то же значение, что и в конструкторе
Unpickler.Изменено в версии 3.8: Добавлен аргумент buffers.
-
pickle.loads(data, /, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None) -
Вернуть восстановленную иерархию объектов из сериализованного представления объекта data. data должен быть объектом, подобным bytes.
Версия протокола pickle определяется автоматически, поэтому указывать аргумент протокола не требуется. Байты после сериализованного представления объекта игнорируются.
Аргументы fix_imports, encoding, errors, strict и buffers имеют то же значение, что и в конструкторе
Unpickler.Изменено в версии 3.8: Добавлен аргумент buffers.
Модуль pickle определяет три исключения:
-
exception pickle.PickleError -
Общий базовый класс для остальных исключений, связанных с pickle. Наследуется от
Exception.
-
exception pickle.PicklingError -
Ошибка, возникающая, когда
Picklerвстречает объект, который нельзя сериализовать с помощью pickle. Наследуется отPickleError.Сведения о том, какие объекты можно сериализовать с помощью pickle, см. в разделе Какие объекты можно сериализовать и десериализовать с помощью pickle?.
-
exception pickle.UnpicklingError -
Ошибка, возникающая при проблеме с десериализацией объекта, например из-за повреждения данных или нарушения безопасности. Наследуется от
PickleError.Обратите внимание, что при десериализации также могут возникать другие исключения, включая, помимо прочих, AttributeError, EOFError, ImportError и IndexError.
Модуль pickle экспортирует три класса: Pickler, Unpickler и PickleBuffer:
-
class pickle.Pickler(file, protocol=None, *, fix_imports=True, buffer_callback=None) -
Принимает двоичный файл для записи потока данных pickle.
Необязательный аргумент protocol — целое число, указывающее сериализатору pickle использовать заданный протокол; поддерживаются протоколы от 0 до
HIGHEST_PROTOCOL. Если аргумент не указан, используется значениеDEFAULT_PROTOCOL. Если указано отрицательное число, выбираетсяHIGHEST_PROTOCOL.Аргумент file должен иметь метод write(), принимающий один аргумент типа bytes. Таким образом, это может быть дисковый файл, открытый для записи в двоичном режиме, экземпляр
io.BytesIOили любой другой пользовательский объект, соответствующий этому интерфейсу.Если fix_imports имеет значение true, а protocol меньше 3, pickle попытается сопоставить новые имена Python 3 со старыми именами модулей, используемыми в Python 2, чтобы поток данных pickle можно было прочитать в Python 2.
Если buffer_callback равен
None(значение по умолчанию), представления буферов сериализуются в file как часть потока pickle.Если buffer_callback не равен
None, его можно вызывать произвольное число раз с представлением буфера. Если обратный вызов возвращает ложное значение (например,None), указанный буфер передаётся вне основного потока; в противном случае буфер сериализуется в основном потоке, то есть внутри потока pickle.Если buffer_callback не равен
None, а protocol равенNoneили меньше 5, возникает ошибка.Изменено в версии 3.8: Добавлен аргумент buffer_callback.
-
dump(obj) -
Записать сериализованное представление obj в открытый файловый объект, указанный в конструкторе.
-
persistent_id(obj) -
По умолчанию ничего не делает. Этот метод предназначен для переопределения в подклассе.
Если
persistent_id()возвращаетNone, obj сериализуется с помощью pickle обычным образом. Любое другое значение приводит к тому, чтоPicklerвыдаёт возвращённое значение в качестве постоянного идентификатора для obj. Значение этого постоянного идентификатора должно определяться методомUnpickler.persistent_load(). Обратите внимание, что значение, возвращаемоеpersistent_id(), само не может иметь постоянный идентификатор.Подробные сведения и примеры использования см. в разделе Сохранение внешних объектов.
Изменено в версии 3.13: Добавлена реализация этого метода по умолчанию в реализации
Picklerна языке C.
-
dispatch_table -
Таблица диспетчеризации объекта сериализатора pickle — это реестр функций редукции, подобных тем, что можно объявить с помощью
copyreg.pickle(). Это отображение, в котором ключами являются классы, а значениями — функции редукции. Функция редукции принимает один аргумент соответствующего класса и должна соответствовать тому же интерфейсу, что и метод__reduce__().По умолчанию у объекта сериализатора pickle нет атрибута
dispatch_table, поэтому он использует глобальную таблицу диспетчеризации, которой управляет модульcopyreg. Однако, чтобы настроить сериализацию с помощью pickle для конкретного объекта сериализатора, можно установить для атрибутаdispatch_tableзначение, подобное словарю. Кроме того, если подклассPicklerимеет атрибутdispatch_table, он будет использоваться в качестве таблицы диспетчеризации по умолчанию для экземпляров этого класса.Примеры использования см. в разделе Таблицы диспетчеризации.
Добавлено в версии 3.3.
-
reducer_override(obj) -
Специальная функция редукции, которую можно определить в подклассах
Pickler. Этот метод имеет приоритет над любыми функциями редукции вdispatch_table. Он должен соответствовать тому же интерфейсу, что и метод__reduce__(), и при необходимости может вернутьNotImplemented, чтобы при сериализации с помощью pickleobjиспользовать функции редукции, зарегистрированные вdispatch_table.Подробный пример см. в разделе Пользовательская редукция типов, функций и других объектов.
Добавлено в версии 3.8.
-
fast -
Устарело. Включает быстрый режим, если установлено истинное значение. Быстрый режим отключает использование memo и тем самым ускоряет процесс сериализации с помощью pickle, не создавая избыточные коды операций PUT. Не следует использовать этот режим с объектами, ссылающимися на себя: в противном случае
Picklerбудет рекурсивно вызываться бесконечно.Если нужны более компактные данные pickle, используйте
pickletools.optimize().
-
-
class pickle.Unpickler(file, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None) -
Принимает двоичный файл для чтения потока данных pickle.
Версия протокола pickle определяется автоматически, поэтому указывать аргумент протокола не требуется.
Аргумент file должен иметь три метода: read(), принимающий целочисленный аргумент, readinto(), принимающий аргумент-буфер, и readline(), не требующий аргументов, как в интерфейсе
io.BufferedIOBase. Таким образом, file может быть дисковым файлом, открытым для чтения в двоичном режиме, объектомio.BytesIOили любым другим пользовательским объектом, соответствующим этому интерфейсу.Необязательные аргументы fix_imports, encoding и errors используются для управления поддержкой совместимости с потоками pickle, созданными в Python 2. Если fix_imports имеет значение true, pickle попытается сопоставить старые имена Python 2 с новыми именами, используемыми в Python 3. Аргументы encoding и errors указывают pickle, как декодировать 8-битные строки, сериализованные с помощью pickle в Python 2; по умолчанию для них используются значения ‘ASCII’ и ‘strict’ соответственно. Для чтения таких 8-битных строк как объектов bytes аргументу encoding можно задать значение ‘bytes’. Для десериализации с помощью pickle массивов NumPy и экземпляров
datetime,dateиtime, сериализованных в Python 2, требуется использоватьencoding='latin1'.Если buffers равен
None(значение по умолчанию), все данные, необходимые для десериализации, должны содержаться в потоке pickle. Это означает, что при созданииPickler(или при вызовеdump()илиdumps()) аргументу buffer_callback было задано значениеNone.Если buffers не равен
None, это должен быть итерируемый объект с поддержкой буферов, элементы которого используются каждый раз, когда поток pickle ссылается на представление буфера, переданного вне основного потока. Такие буферы были переданы по порядку аргументу buffer_callback объекта Pickler.Изменено в версии 3.8: Добавлен аргумент buffers.
-
load() -
Прочитать сериализованное представление объекта из открытого файлового объекта, указанного в конструкторе, и вернуть заданную в нём восстановленную иерархию объектов. Байты после сериализованного представления объекта игнорируются.
-
persistent_load(pid) -
По умолчанию вызывает исключение
UnpicklingError.Если метод
persistent_load()определён, он должен возвращать объект, заданный постоянным идентификатором pid. Если встретится недопустимый постоянный идентификатор, следует вызвать исключениеUnpicklingError.Подробные сведения и примеры использования см. в разделе Сохранение внешних объектов.
Изменено в версии 3.13: Добавлена реализация этого метода по умолчанию в реализации
Unpicklerна языке C.
-
find_class(module, name) -
При необходимости импортировать module и вернуть из него объект с именем name; аргументы module и name должны быть объектами
str. Обратите внимание: несмотря на название,find_class()также используется для поиска функций.Подклассы могут переопределить этот метод, чтобы контролировать, объекты каких типов и каким образом можно загружать, потенциально снижая риски для безопасности. Подробности см. в разделе Ограничение глобальных объектов.
Вызывает событие аудита
pickle.find_classс аргументамиmodule,name.
-
-
class pickle.PickleBuffer(buffer) -
Обёртка для буфера, представляющего сериализуемые с помощью pickle данные. buffer должен быть объектом, предоставляющим буфер, например объектом, подобным bytes или N-мерным массивом.
PickleBufferсам предоставляет буфер, поэтому его можно передавать другим API, ожидающим объект, предоставляющий буфер, напримерmemoryview.Объекты
PickleBufferможно сериализовать только с помощью протокола pickle версии 5 или выше. Они подходят для сериализации вне основного потока.Добавлено в версии 3.8.
-
raw() -
Вернуть
memoryviewобласти памяти, лежащей в основе этого буфера. Возвращаемый объект — одномерное, непрерывное в памяти представление памяти C с форматомB(беззнаковые байты). Если буфер не является непрерывным ни в формате C, ни в формате Fortran, вызывается исключениеBufferError.
-
release() -
Освободить базовый буфер, предоставляемый объектом PickleBuffer.
-
Какие объекты можно сериализовать и десериализовать с помощью pickle?
С помощью pickle можно сериализовать следующие типы:
- встроенные константы (
None,True,False,EllipsisиNotImplemented); - целые числа, числа с плавающей точкой, комплексные числа;
- строки, объекты bytes и bytearray;
- кортежи, списки, множества и словари, содержащие только сериализуемые с помощью pickle объекты;
- функции (встроенные и пользовательские), доступные на верхнем уровне модуля (с использованием
def, но неlambda); - классы, доступные на верхнем уровне модуля;
- экземпляры таких классов, результат вызова
__getstate__()для которых можно сериализовать с помощью pickle (подробности см. в разделе Сериализация экземпляров классов с помощью pickle).
Попытка сериализовать с помощью pickle объект, который нельзя сериализовать таким образом, приводит к исключению PicklingError; к этому моменту в базовый файл уже может быть записано неопределённое количество байтов. Попытка сериализовать с помощью pickle глубоко рекурсивную структуру данных может превысить максимальную глубину рекурсии; в этом случае будет вызвано исключение RecursionError. Это ограничение можно осторожно увеличить с помощью sys.setrecursionlimit().
Обратите внимание, что функции (встроенные и пользовательские) сериализуются с помощью pickle по полному квалифицированному имени, а не по значению. [2] Это означает, что сериализуется только имя функции, а также имена содержащего её модуля и классов. Ни код функции, ни какие-либо её атрибуты не сериализуются. Поэтому определяющий модуль должен быть доступен для импорта в среде десериализации, а модуль должен содержать объект с указанным именем; в противном случае будет вызвано исключение. [3]
Аналогично, классы сериализуются с помощью pickle по полному квалифицированному имени, поэтому в среде десериализации действуют те же ограничения. Обратите внимание, что код и данные класса не сериализуются, поэтому в следующем примере атрибут класса attr не восстанавливается в среде десериализации:
class Foo:
attr = 'A class attribute'
picklestring = pickle.dumps(Foo)
Именно поэтому сериализуемые с помощью pickle функции и классы должны быть определены на верхнем уровне модуля.
Аналогично, при сериализации экземпляров классов с помощью pickle код и данные их классов не сериализуются вместе с ними. Сериализуются только данные экземпляра. Это сделано намеренно: так можно исправлять ошибки в классе или добавлять в него методы и при этом загружать объекты, созданные с помощью предыдущей версии класса. Если предполагается, что объекты будут существовать долго и за это время класс претерпит много изменений, может быть полезно добавить в объекты номер версии, чтобы метод __setstate__() класса мог выполнять необходимые преобразования.
Сериализация экземпляров классов
В этом разделе описаны общие механизмы, с помощью которых можно определять, настраивать и контролировать сериализацию и десериализацию экземпляров классов.
В большинстве случаев для того, чтобы экземпляры можно было сериализовать, дополнительный код не требуется. По умолчанию pickle получает класс и атрибуты экземпляра с помощью интроспекции. При десериализации экземпляра класса его метод __init__() обычно не вызывается. По умолчанию сначала создаётся неинициализированный экземпляр, а затем восстанавливаются сохранённые атрибуты. Следующий код показывает реализацию такого поведения:
def save(obj):
return (obj.__class__, obj.__dict__)
def restore(cls, attributes):
obj = cls.__new__(cls)
obj.__dict__.update(attributes)
return obj
Классы могут изменять поведение по умолчанию, определяя один или несколько специальных методов:
-
object.__getnewargs_ex__() -
В протоколах 2 и более новых классам, реализующим метод
__getnewargs_ex__(), разрешено задавать значения, передаваемые методу__new__()при десериализации. Метод должен возвращать пару(args, kwargs), где args — кортеж позиционных аргументов, а kwargs — словарь именованных аргументов для создания объекта. Они будут переданы методу__new__()при десериализации.Этот метод следует реализовать, если метод
__new__()вашего класса требует аргументы, передаваемые только по имени. В противном случае для обеспечения совместимости рекомендуется реализовать__getnewargs__().Изменено в версии 3.6: Теперь
__getnewargs_ex__()используется в протоколах 2 и 3.
-
object.__getnewargs__() -
Этот метод служит той же цели, что и
__getnewargs_ex__(), но поддерживает только позиционные аргументы. Он должен возвращать кортеж аргументовargs, который будет передан методу__new__()при десериализации.__getnewargs__()не будет вызван, если определён__getnewargs_ex__().Изменено в версии 3.6: До Python 3.6 в протоколах 2 и 3 вместо
__getnewargs_ex__()вызывался__getnewargs__().
-
object.__getstate__() -
Классы могут дополнительно влиять на сериализацию своих экземпляров, переопределив метод
__getstate__(). Он вызывается, а возвращённый объект сериализуется как содержимое экземпляра вместо состояния по умолчанию. Возможны несколько случаев:- Для класса без экземплярного атрибута
__dict__и без__slots__состояние по умолчанию —None. - Для класса с экземплярным атрибутом
__dict__и без__slots__состояние по умолчанию —self.__dict__. - Для класса с экземплярным атрибутом
__dict__и__slots__состояние по умолчанию представляет собой кортеж из двух словарей:self.__dict__и словаря, сопоставляющего имена слотов их значениям. В последний включаются только слоты, которым присвоено значение. - Для класса с
__slots__и без экземплярного атрибута__dict__состояние по умолчанию представляет собой кортеж, первый элемент которого —None, а второй — словарь, сопоставляющий имена слотов их значениям, как описано в предыдущем пункте.
Изменено в версии 3.11: Добавлена реализация метода
__getstate__()по умолчанию в классеobject. - Для класса без экземплярного атрибута
-
object.__setstate__(state) -
При десериализации, если класс определяет
__setstate__(), этот метод вызывается с десериализованным состоянием. В таком случае объект состояния не обязан быть словарём. В противном случае сериализованное состояние должно быть словарём, элементы которого присваиваются словарю нового экземпляра.Примечание
Если при сериализации
__reduce__()возвращает состояние со значениемNone, метод__setstate__()при десериализации вызван не будет.
Дополнительную информацию об использовании методов __getstate__() и __setstate__() см. в разделе Обработка объектов с состоянием.
Примечание
При десериализации у экземпляра могут вызываться некоторые методы, например __getattr__(), __getattribute__() или __setattr__(). Если эти методы зависят от соблюдения некоторых внутренних инвариантов, тип должен реализовать __new__(), чтобы установить такие инварианты, поскольку при десериализации экземпляра __init__() не вызывается.
Как мы увидим далее, pickle не использует напрямую описанные выше методы. На самом деле эти методы являются частью протокола копирования, реализующего специальный метод __reduce__(). Протокол копирования предоставляет единый интерфейс для получения данных, необходимых для сериализации и копирования объектов. [4]
Несмотря на широкие возможности, непосредственная реализация __reduce__() в ваших классах чревата ошибками. Поэтому разработчикам классов следует по возможности использовать высокоуровневый интерфейс (то есть __getnewargs_ex__(), __getstate__() и __setstate__()). Тем не менее мы рассмотрим случаи, когда использование __reduce__() является единственным вариантом или обеспечивает более эффективную сериализацию — либо и то и другое.
-
object.__reduce__() -
В настоящее время интерфейс определён следующим образом. Метод
__reduce__()не принимает аргументов и должен возвращать либо строку, либо, предпочтительно, кортеж (возвращаемый объект часто называют «значением reduce»).Если возвращается строка, её следует интерпретировать как имя глобальной переменной. Это должно быть локальное имя объекта относительно его модуля; модуль pickle ищет объект в пространстве имён модуля, чтобы определить модуль объекта. Такое поведение обычно полезно для одиночек.
Если возвращается кортеж, он должен содержать от двух до шести элементов. Необязательные элементы можно опустить либо указать в качестве их значения
None. Значение каждого элемента по порядку:- Вызываемый объект, который будет вызван для создания начальной версии объекта.
- Кортеж аргументов для вызываемого объекта. Если вызываемый объект не принимает аргументов, необходимо передать пустой кортеж.
- Необязательно — состояние объекта, которое будет передано методу объекта
__setstate__(), как описано выше. Если у объекта нет такого метода, значение должно быть словарём и будет добавлено в атрибут объекта__dict__. - Необязательно — итератор (не последовательность), выдающий последовательные элементы. Эти элементы будут добавлены к объекту по одному с помощью
obj.append(item)или пакетно с помощьюobj.extend(list_of_items). Это используется главным образом для подклассов списков, но может применяться и для других классов, если у них есть методыappend()иextend()с подходящей сигнатурой. (Использованиеappend()илиextend()зависит от версии протокола pickle и количества добавляемых элементов, поэтому должны поддерживаться оба метода.) - Необязательно — итератор (не последовательность), выдающий последовательные пары ключ-значение. Эти элементы будут сохранены в объекте с помощью
obj[key] = value. Это используется главным образом для подклассов словарей, но может применяться и для других классов, если они реализуют__setitem__(). -
Необязательно — вызываемый объект с сигнатурой
(obj, state). Этот вызываемый объект позволяет программно управлять поведением обновления состояния конкретного объекта вместо использования статического методаobj__setstate__(). Если значение не равноNone, этот вызываемый объект имеет приоритет над методомobj__setstate__().Добавлено в версии 3.8: Добавлен необязательный шестой элемент кортежа,
(obj, state).
-
object.__reduce_ex__(protocol) -
В качестве альтернативы можно определить метод
__reduce_ex__(). Единственное отличие состоит в том, что этот метод должен принимать один целочисленный аргумент — версию протокола. Если он определён, pickle отдаёт ему предпочтение перед методом__reduce__(). Кроме того,__reduce__()автоматически становится синонимом расширенной версии. Основное назначение этого метода — предоставление обратно совместимых значений reduce для старых выпусков Python.
Сохранение внешних объектов
Для сохранения объектов модуль pickle поддерживает понятие ссылки на объект, находящийся вне потока сериализованных данных. На такие объекты ссылаются с помощью постоянного идентификатора, которым должна быть либо строка из букв и цифр (для протокола 0) [5], либо произвольный объект (для любого более нового протокола).
Определение постоянных идентификаторов не входит в обязанности модуля pickle; он делегирует разрешение таких идентификаторов определённым пользователем методам сериализатора и десериализатора — соответственно persistent_id() и persistent_load().
Чтобы сериализовать объекты с внешним постоянным идентификатором, сериализатор должен иметь пользовательский метод persistent_id(), который принимает объект в качестве аргумента и возвращает либо None, либо постоянный идентификатор этого объекта. Если возвращается None, сериализатор просто сериализует объект обычным образом. Если возвращается строка с постоянным идентификатором, сериализатор сериализует объект вместе с маркером, позволяющим десериализатору распознать этот идентификатор как постоянный.
Чтобы десериализовать внешние объекты, десериализатор должен иметь пользовательский метод persistent_load(), который принимает объект — постоянный идентификатор — и возвращает объект, на который тот ссылается.
Ниже приведён подробный пример сериализации внешних объектов по ссылке с помощью постоянных идентификаторов.
# Simple example presenting how persistent ID can be used to pickle
# external objects by reference.
import pickle
import sqlite3
from collections import namedtuple
# Simple class representing a record in our database.
MemoRecord = namedtuple("MemoRecord", "key, task")
class DBPickler(pickle.Pickler):
def persistent_id(self, obj):
# Instead of pickling MemoRecord as a regular class instance, we emit a
# persistent ID.
if isinstance(obj, MemoRecord):
# Here, our persistent ID is simply a tuple, containing a tag and a
# key, which refers to a specific record in the database.
return ("MemoRecord", obj.key)
else:
# If obj does not have a persistent ID, return None. This means obj
# needs to be pickled as usual.
return None
class DBUnpickler(pickle.Unpickler):
def __init__(self, file, connection):
super().__init__(file)
self.connection = connection
def persistent_load(self, pid):
# This method is invoked whenever a persistent ID is encountered.
# Here, pid is the tuple returned by DBPickler.
cursor = self.connection.cursor()
type_tag, key_id = pid
if type_tag == "MemoRecord":
# Fetch the referenced record from the database and return it.
cursor.execute("SELECT * FROM memos WHERE key=?", (str(key_id),))
key, task = cursor.fetchone()
return MemoRecord(key, task)
else:
# Always raises an error if you cannot return the correct object.
# Otherwise, the unpickler will think None is the object referenced
# by the persistent ID.
raise pickle.UnpicklingError("unsupported persistent object")
def main():
import io
import pprint
# Initialize and populate our database.
conn = sqlite3.connect(":memory:")
cursor = conn.cursor()
cursor.execute("CREATE TABLE memos(key INTEGER PRIMARY KEY, task TEXT)")
tasks = (
'give food to fish',
'prepare group meeting',
'fight with a zebra',
)
for task in tasks:
cursor.execute("INSERT INTO memos VALUES(NULL, ?)", (task,))
# Fetch the records to be pickled.
cursor.execute("SELECT * FROM memos")
memos = [MemoRecord(key, task) for key, task in cursor]
# Save the records using our custom DBPickler.
file = io.BytesIO()
DBPickler(file).dump(memos)
print("Pickled records:")
pprint.pprint(memos)
# Update a record, just for good measure.
cursor.execute("UPDATE memos SET task='learn italian' WHERE key=1")
# Load the records from the pickle data stream.
file.seek(0)
memos = DBUnpickler(file, conn).load()
print("Unpickled records:")
pprint.pprint(memos)
if __name__ == '__main__':
main()
Таблицы диспетчеризации
Если нужно настроить сериализацию некоторых классов, не затрагивая другой код, который зависит от сериализации, можно создать сериализатор с собственной таблицей диспетчеризации.
Глобальная таблица диспетчеризации, управляемая модулем copyreg, доступна как copyreg.dispatch_table. Поэтому можно использовать изменённую копию copyreg.dispatch_table в качестве собственной таблицы диспетчеризации.
Например:
f = io.BytesIO() p = pickle.Pickler(f) p.dispatch_table = copyreg.dispatch_table.copy() p.dispatch_table[SomeClass] = reduce_SomeClass
создаёт экземпляр pickle.Pickler с собственной таблицей диспетчеризации, которая специально обрабатывает класс SomeClass. В качестве альтернативы следующий код
class MyPickler(pickle.Pickler):
dispatch_table = copyreg.dispatch_table.copy()
dispatch_table[SomeClass] = reduce_SomeClass
f = io.BytesIO()
p = MyPickler(f)
делает то же самое, но все экземпляры MyPickler по умолчанию будут совместно использовать собственную таблицу диспетчеризации. В свою очередь, следующий код
copyreg.pickle(SomeClass, reduce_SomeClass) f = io.BytesIO() p = pickle.Pickler(f)
изменяет глобальную таблицу диспетчеризации, общую для всех пользователей модуля copyreg.
Обработка объектов с состоянием
Вот пример изменения поведения сериализации класса. Класс TextReader ниже открывает текстовый файл и при каждом вызове метода readline() возвращает номер строки и её содержимое. При сериализации экземпляра TextReader сохраняются все атрибуты, кроме атрибута — файлового объекта. При десериализации файл открывается повторно, а чтение продолжается с последней позиции. Для реализации такого поведения используются методы __setstate__() и __getstate__().
class TextReader:
"""Print and number lines in a text file."""
def __init__(self, filename):
self.filename = filename
self.file = open(filename)
self.lineno = 0
def readline(self):
self.lineno += 1
line = self.file.readline()
if not line:
return None
if line.endswith('\n'):
line = line[:-1]
return "%i: %s" % (self.lineno, line)
def __getstate__(self):
# Copy the object's state from self.__dict__ which contains
# all our instance attributes. Always use the dict.copy()
# method to avoid modifying the original state.
state = self.__dict__.copy()
# Remove the unpicklable entries.
del state['file']
return state
def __setstate__(self, state):
# Restore instance attributes (i.e., filename and lineno).
self.__dict__.update(state)
# Restore the previously opened file's state. To do so, we need to
# reopen it and read from it until the line count is restored.
file = open(self.filename)
for _ in range(self.lineno):
file.readline()
# Finally, save the file.
self.file = file
Пример использования может выглядеть так:
>>> reader = TextReader("hello.txt")
>>> reader.readline()
'1: Hello world!'
>>> reader.readline()
'2: I am line number two.'
>>> new_reader = pickle.loads(pickle.dumps(reader))
>>> new_reader.readline()
'3: Goodbye!'
Пользовательское сведение типов, функций и других объектов
Добавлено в версии 3.8.
В некоторых случаях dispatch_table может оказаться недостаточно гибким. В частности, может потребоваться настраивать сериализацию по критерию, отличному от типа объекта, либо настраивать сериализацию функций и классов.
В таких случаях можно создать подкласс Pickler и реализовать метод reducer_override(). Этот метод может возвращать произвольный кортеж сведения (см. __reduce__()). Также он может возвращать NotImplemented, чтобы вернуться к традиционному поведению.
Если определены и dispatch_table, и reducer_override(), приоритет имеет метод reducer_override().
Примечание
В целях повышения производительности reducer_override() может не вызываться для следующих объектов: None, True, False и точных экземпляров int, float, bytes, str, dict, set, frozenset, list и tuple.
Вот простой пример, в котором мы разрешаем сериализацию и восстановление заданного класса:
import io
import pickle
class MyClass:
my_attribute = 1
class MyPickler(pickle.Pickler):
def reducer_override(self, obj):
"""Custom reducer for MyClass."""
if getattr(obj, "__name__", None) == "MyClass":
return type, (obj.__name__, obj.__bases__,
{'my_attribute': obj.my_attribute})
else:
# For any other object, fallback to usual reduction
return NotImplemented
f = io.BytesIO()
p = MyPickler(f)
p.dump(MyClass)
del MyClass
unpickled_class = pickle.loads(f.getvalue())
assert isinstance(unpickled_class, type)
assert unpickled_class.__name__ == "MyClass"
assert unpickled_class.my_attribute == 1
Буферы вне основного потока
Добавлено в версии 3.8.
В некоторых случаях модуль pickle используется для передачи больших объёмов данных. Поэтому для сохранения производительности и экономии ресурсов может быть важно свести к минимуму количество копирований памяти. Однако обычная работа модуля pickle, преобразующего графоподобную структуру объектов в последовательный поток байтов, неизбежно включает копирование данных в поток pickle и обратно.
От этого ограничения можно избавиться, если и поставщик (реализация типов объектов, которые требуется передать), и получатель (реализация системы связи) поддерживают средства передачи вне основного потока, предоставляемые протоколом pickle 5 и выше.
API поставщика
Сериализуемые объекты с большим объёмом данных должны реализовать метод __reduce_ex__(), специализированный для протокола 5 и выше, который возвращает экземпляр PickleBuffer (вместо, например, объекта bytes) для каждого большого объёма данных.
Объект PickleBuffer указывает, что базовый буфер можно передавать вне основного потока данных. Такие объекты остаются совместимыми с обычным использованием модуля pickle. Однако получатели также могут явно сообщить модулю pickle, что будут самостоятельно обрабатывать эти буферы.
API получателя
Система связи может включить пользовательскую обработку объектов PickleBuffer, создаваемых при сериализации графа объектов.
На стороне отправителя необходимо передать аргумент buffer_callback объекту Pickler (или функции dump() либо dumps()). Он будет вызываться для каждого объекта PickleBuffer, созданного при сериализации графа объектов. Данные буферов, накопленных с помощью buffer_callback, не копируются в поток pickle — вместо них вставляется компактный маркер.
На стороне получателя необходимо передать аргумент buffers объекту Unpickler (или функции load() либо loads()). Это итерируемый объект, содержащий буферы, переданные в buffer_callback. Он должен выдавать буферы в том же порядке, в каком они были переданы в buffer_callback. Эти буферы предоставляют данные, необходимые для восстановления объектов, сериализация которых создала исходные объекты PickleBuffer.
Между отправителем и получателем система связи может реализовать собственный механизм передачи буферов вне основного потока. Среди возможных оптимизаций — использование разделяемой памяти или сжатия с учётом типа данных.
Пример
Вот простой пример реализации подкласса bytearray, поддерживающего сериализацию буферов вне основного потока:
class ZeroCopyByteArray(bytearray):
def __reduce_ex__(self, protocol):
if protocol >= 5:
return type(self)._reconstruct, (PickleBuffer(self),), None
else:
# PickleBuffer is forbidden with pickle protocols <= 4.
return type(self)._reconstruct, (bytearray(self),)
@classmethod
def _reconstruct(cls, obj):
with memoryview(obj) as m:
# Get a handle over the original buffer object
obj = m.obj
if type(obj) is cls:
# Original buffer object is a ZeroCopyByteArray, return it
# as-is.
return obj
else:
return cls(obj)
Функция восстановления (метод класса _reconstruct) возвращает объект, предоставляющий буфер, если он имеет нужный тип. Это простой способ смоделировать поведение без копирования в этом игрушечном примере.
На стороне получателя мы можем сериализовать эти объекты обычным способом; после десериализации будет получена копия исходного объекта:
b = ZeroCopyByteArray(b"abc") data = pickle.dumps(b, protocol=5) new_b = pickle.loads(data) print(b == new_b) # True print(b is new_b) # False: a copy was made
Однако, передав buffer_callback и затем вернув накопленные буферы при десериализации, мы можем получить исходный объект:
b = ZeroCopyByteArray(b"abc") buffers = [] data = pickle.dumps(b, protocol=5, buffer_callback=buffers.append) new_b = pickle.loads(data, buffers=buffers) print(b == new_b) # True print(b is new_b) # True: no copy was made
Этот пример ограничен тем, что bytearray выделяет собственную память: создать экземпляр bytearray, использующий память другого объекта, нельзя. Однако сторонние типы данных, например массивы NumPy, такого ограничения не имеют и позволяют сериализовать данные без копирования (или с минимальным количеством копий) при передаче между разными процессами или системами.
См. также
PEP 574 — протокол pickle 5 с данными вне основного потока
Ограничение глобальных объектов
По умолчанию при десериализации импортируется любой класс или функция, найденные в данных pickle. Для многих приложений такое поведение неприемлемо, поскольку оно позволяет десериализатору импортировать и вызывать произвольный код. Рассмотрим, что делает этот созданный вручную поток данных pickle при загрузке:
>>> import pickle >>> pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.") hello world 0
В этом примере десериализатор импортирует функцию os.system() и применяет к ней строковый аргумент «echo hello world». Хотя этот пример безвреден, нетрудно представить пример, который может повредить вашу систему.
Поэтому может потребоваться контролировать десериализуемые объекты, настроив Unpickler.find_class(). Несмотря на название, Unpickler.find_class() вызывается всякий раз, когда запрашивается глобальный объект (то есть класс или функция). Таким образом, можно либо полностью запретить глобальные объекты, либо ограничить их безопасным подмножеством.
Ниже приведён пример десериализатора, который разрешает загружать лишь несколько безопасных классов из модуля builtins:
import builtins
import io
import pickle
safe_builtins = {
'range',
'complex',
'set',
'frozenset',
'slice',
}
class RestrictedUnpickler(pickle.Unpickler):
def find_class(self, module, name):
# Only allow safe classes from builtins.
if module == "builtins" and name in safe_builtins:
return getattr(builtins, name)
# Forbid everything else.
raise pickle.UnpicklingError("global '%s.%s' is forbidden" %
(module, name))
def restricted_loads(s):
"""Helper function analogous to pickle.loads()."""
return RestrictedUnpickler(io.BytesIO(s)).load()
Пример использования нашего десериализатора, работающего как задумано:
>>> restricted_loads(pickle.dumps([1, 2, range(15)]))
[1, 2, range(0, 15)]
>>> restricted_loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
Traceback (most recent call last):
...
pickle.UnpicklingError: global 'os.system' is forbidden
>>> restricted_loads(b'cbuiltins\neval\n'
... b'(S\'getattr(__import__("os"), "system")'
... b'("echo hello world")\'\ntR.')
Traceback (most recent call last):
...
pickle.UnpicklingError: global 'builtins.eval' is forbidden
Как показывают наши примеры, необходимо внимательно относиться к тому, какие объекты разрешено десериализовать. Поэтому, если безопасность вызывает опасения, стоит рассмотреть альтернативы, например API маршалинга в xmlrpc.client или сторонние решения.
Производительность
В последних версиях протокола pickle (начиная с протокола 2) предусмотрены эффективные двоичные представления для ряда распространённых возможностей и встроенных типов. Кроме того, модуль pickle содержит прозрачный оптимизатор, написанный на C.
Примеры
Для самого простого кода используйте функции dump() и load().
import pickle
# An arbitrary collection of objects supported by pickle.
data = {
'a': [1, 2.0, 3+4j],
'b': ("character string", b"byte string"),
'c': {None, True, False}
}
with open('data.pickle', 'wb') as f:
# Pickle the 'data' dictionary using the highest protocol available.
pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)
В следующем примере считываются полученные сериализованные данные.
import pickle
with open('data.pickle', 'rb') as f:
# The protocol version used is detected automatically, so we do not
# have to specify it.
data = pickle.load(f)
См. также
-
Modulecopyreg -
Регистрация конструкторов интерфейса Pickle для типов расширений.
-
Modulepickletools -
Инструменты для работы с сериализованными данными и их анализа.
-
Moduleshelve -
Индексированные базы данных объектов; используется
pickle. -
Modulecopy -
Поверхностное и глубокое копирование объектов.
-
Modulemarshal -
Высокопроизводительная сериализация встроенных типов.
Сноски
© 2001–2025 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/library/pickle.html