pickle — Сериализация объектов Python
Исходный код: Lib/pickle.py
Модуль pickle реализует двоичные протоколы для сериализации и десериализации структуры объектов Python. «Заморозка» — процесс преобразования иерархии объектов Python в поток байтов, а «разморозка» — обратная операция, при которой поток байтов (из двоичного файла или объекта-подобного байтам) преобразуется обратно в иерархию объектов. Заморозку (и разморозку) также называют «сериализацией», «маршалингом», 1 или «выравниванием»; однако, чтобы избежать путаницы, здесь используются термины «заморозка» и «разморозка».
Предупреждение
Модуль pickle небезопасен. Размораживайте только данные, которым вы доверяете.
Возможна конструкция вредоносных данных pickle, которые будут выполнять произвольный код во время разморозки. Никогда не размораживайте данные, которые могут исходить из недоверенного источника или быть изменены.
Рассмотрите возможность подписи данных с помощью hmac, если вам нужно убедиться, что они не были изменены.
Более безопасные форматы сериализации, такие как json, могут быть более подходящими, если вы обрабатываете недоверенные данные. См. Сравнение с json.
Связь с другими модулями Python
Сравнение с marshal
В Python есть более примитивный модуль сериализации, называемый marshal, но в общем случае pickle всегда должен быть предпочтительным способом сериализации объектов Python. marshal существует в основном для поддержки файлов Python .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 Object Notation):
- JSON — текстовый формат сериализации (он выводит текст unicode, хотя чаще всего он затем кодируется в
utf-8), а pickle — двоичный формат сериализации; - JSON удобочитаем, а pickle — нет;
- JSON является взаимозаменяемым и широко используется за пределами экосистемы Python, а pickle является специфичным для Python;
- JSON по умолчанию может представлять только подмножество встроенных типов Python и не поддерживает пользовательские классы; pickle может представлять чрезвычайно большое количество типов Python (многие из них автоматически, благодаря использованию средств интроспекции Python; сложные случаи могут быть решены путем реализации специфических API для объектов);
- В отличие от pickle, десериализация недоверенных данных JSON сама по себе не создает уязвимости выполнения произвольного кода.
См. также
Модуль json: стандартный модуль, позволяющий сериализацию и десериализацию JSON.
Формат потока данных
Формат данных, используемый pickle, специфичен для Python. Это даёт преимущество отсутствия ограничений, накладываемых внешними стандартами, такими как JSON или XDR (которые не могут представлять совместное использование указателей); однако это означает, что программы, не написанные на Python, могут не быть способны восстановить закодированные Python объекты.
По умолчанию, формат данных pickle использует относительно компактное двоичное представление. Если вам необходимы оптимальные характеристики размера, вы можете эффективно сжать закодированные данные.
Модуль pickletools содержит инструменты для анализа потоков данных, генерируемых pickle. Исходный код pickletools содержит обширные комментарии об операционных кодах, используемых протоколами pickle.
В настоящее время доступны 6 различных протоколов, которые могут быть использованы для кодирования. Чем выше используемый протокол, тем более поздняя версия Python требуется для чтения сгенерированного pickle.
- Протокол версии 0 — это оригинальный «человекочитаемый» протокол и обратно совместим с более ранними версиями Python.
- Протокол версии 1 — это старый двоичный формат, также совместимый с более ранними версиями Python.
- Протокол версии 2 был представлен в Python 2.3. Он обеспечивает гораздо более эффективное кодирование классов нового стиля. Обратитесь к PEP 307 за информацией об улучшениях, принесённых протоколом 2.
- Протокол версии 3 был добавлен в Python 3.0. Он имеет явную поддержку для
bytesобъектов и не может быть распакован Python 2.x. Это был протокол по умолчанию в Python 3.0–3.7. - Протокол версии 4 был добавлен в Python 3.4. Он добавляет поддержку очень больших объектов, кодирования большего количества типов объектов и некоторых оптимизаций формата данных. Это протокол по умолчанию, начиная с Python 3.8. Обратитесь к PEP 3154 за информацией об улучшениях, принесённых протоколом 4.
- Протокол версии 5 был добавлен в Python 3.8. Он добавляет поддержку данных вне зоны доступа и ускорение для данных в зоне доступа. Обратитесь к PEP 574 за информацией об улучшениях, принесённых протоколом 5.
Примечание
Сериализация — это более примитивное понятие, чем персистенция; хотя pickle читает и записывает объекты файлов, он не обрабатывает проблему именования постоянных объектов, ни (ещё более сложную) проблему одновременного доступа к постоянным объектам. Модуль pickle может преобразовать сложный объект в поток байтов, и он может преобразовать поток байтов в объект с той же внутренней структурой. Возможно, самым очевидным действием с этими потоками байтов является запись их в файл, но также можно отправить их через сеть или сохранить в базе данных. Модуль shelve предоставляет простой интерфейс для кодирования и декодирования объектов в файлах баз данных в стиле DBM.
Интерфейс модуля
Для сериализации иерархии объектов, просто вызовите функцию dumps(). Аналогично, для десериализации потока данных, вызовите функцию loads(). Однако, если вы хотите иметь больший контроль над сериализацией и десериализацией, вы можете создать объект Pickler или Unpickler соответственно.
Модуль pickle предоставляет следующие константы:
-
pickle.HIGHEST_PROTOCOL -
Целое число, максимальная доступная версия протокола. Это значение может быть передано в качестве значения protocol функциям
dump()иdumps(), а также конструкторуPickler.
-
pickle.DEFAULT_PROTOCOL -
Целое число, используемая по умолчанию версия протокола для сериализации. Может быть меньше
HIGHEST_PROTOCOL. В настоящее время протокол по умолчанию — 4, введенный в Python 3.4, и несовместимый с предыдущими версиями.Изменено в версии 3.0: Протокол по умолчанию — 3.
Изменено в версии 3.8: Протокол по умолчанию — 4.
Модуль 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 -
Базовый класс для других исключений сериализации. Наследует
Exception.
-
exception pickle.PicklingError -
Исключение, выбрасываемое, когда встречается несериализуемый объект, например, конструктором
Pickler. НаследуетPickleError.Обратитесь к Что можно сериализовать и десериализовать?, чтобы узнать, какие виды объектов можно сериализовать.
-
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, целое число, сообщает пиклеру использовать указанный протокол; поддерживаемые протоколы — от 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 сериализуется как обычно. Любое другое значение заставляетPicklerвыводить возвращаемое значение в качестве постоянного идентификатора для obj. Значение этого постоянного идентификатора должно быть определеноUnpickler.persistent_load(). Обратите внимание, что значение, возвращаемоеpersistent_id(), само по себе не может иметь постоянный идентификатор.См. Сохранение внешних объектов для получения подробной информации и примеров использования.
-
dispatch_table -
Таблица распределения объекта пиклера — это реестр _функций сокращения_, которые можно объявить с помощью
copyreg.pickle(). Это отображение, ключами которого являются классы, а значениями — функции сокращения. Функция сокращения принимает один аргумент связанного класса и должна соответствовать тому же интерфейсу, что и метод__reduce__().По умолчанию у объекта пиклера не будет атрибута
dispatch_table, и он будет вместо этого использовать глобальную таблицу распределения, управляемую модулемcopyreg. Однако, чтобы настроить сериализацию для конкретного объекта пиклера, можно установить атрибутdispatch_tableна объект типа dict. В качестве альтернативы, если подклассPicklerимеет атрибутdispatch_table, то он будет использоваться в качестве таблицы распределения по умолчанию для экземпляров этого класса.См. Таблицы распределения для примеров использования.
Добавлена в версии 3.3.
-
reducer_override(obj) -
Специальный редуктор, который можно определить в подклассах
Pickler. Этот метод имеет приоритет над любым редуктором вdispatch_table. Он должен соответствовать тому же интерфейсу, что и метод__reduce__(), и может необязательно вернутьNotImplementedдля возврата к редукторам, зарегистрированным вdispatch_table, для сериализацииobj.Подробный пример см. в разделе Настройка сокращения для типов, функций и других объектов.
Добавлена в версии 3.8.
-
fast -
Устарело. Включить быстрый режим, если значение равно true. Быстрый режим отключает использование запоминающего устройства, поэтому ускоряет процесс сериализации, не генерируя излишние операции PUT. Не следует использовать с объектами, ссылающимися на себя; в противном случае
Picklerбудет рекурсивно вызываться бесконечно.Используйте
pickletools.optimize(), если вам нужны более компактные pickle.
-
-
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 истинно, pickle попытается сопоставить старые имена Python 2 с новыми именами, используемыми в Python 3. encoding и errors сообщают pickle, как декодировать 8-битные строковые экземпляры, заархивированные Python 2; они по умолчанию равны ‘ASCII’ и ‘strict’ соответственно. encoding может быть ‘bytes’, чтобы читать эти 8-битные строковые экземпляры как объекты bytes. Для распаковки массивов NumPy и экземпляров
datetime,dateиtime, заархивированных Python 2, требуется использованиеencoding='latin1'.Если buffers равно None (по умолчанию), то все данные, необходимые для десериализации, должны содержаться в потоке pickle. Это означает, что аргумент buffer_callback был None при создании
Pickler(или при вызовеdump()илиdumps()).Если buffers не равно None, оно должно быть итерируемым объектом с поддержкой буферов, который потребляется каждый раз, когда поток pickle ссылается на представление буфера вне зоны данных. Такие буферы были предоставлены в порядке вызова buffer_callback объекта Pickler.
Изменено в версии 3.8: Добавлен аргумент buffers.
-
load() -
Прочитайте сериализованное представление объекта из открытого объекта файла, указанного в конструкторе, и верните реконструированную иерархию объектов, указанную в нём. Байты после сериализованного представления объекта игнорируются.
-
persistent_load(pid) -
По умолчанию вызывает
UnpicklingError.Если определено,
persistent_load()должно возвращать объект, указанный постоянным идентификатором pid. Если обнаружен неверный постоянный идентификатор, должно быть вызвано исключениеUnpicklingError.Подробнее о применении и примерах см. Сохранение внешних объектов.
-
find_class(module, name) -
Импортирует module при необходимости и возвращает объект с именем name из него, где module и name – объекты
str. Обратите внимание, что, вопреки своему имени,find_class()также используется для поиска функций.Подклассы могут переопределить его, чтобы получить контроль над типами объектов и способом их загрузки, потенциально снижая риски безопасности. Подробнее см. Ограничение глобальных переменных.
Вызывает событие аудита аудита
pickle.find_classс аргументамиmodule,name.
-
-
class pickle.PickleBuffer(buffer) -
Обёртка для буфера, представляющего данные, пригодные для архивирования pickle. buffer должен быть объектом, предоставляющим буфер, например, объектом bytes-like object или многомерным массивом.
PickleBufferсам по себе является поставщиком буфера, поэтому его можно передать другим API, ожидающим объекта, предоставляющего буфер, например,memoryview.Объекты
PickleBufferмогут быть сериализованы только с помощью протокола pickle 5 или выше. Они подходят для сериализации вне зоны данных.Добавлен в версии 3.8.
-
raw() -
Возвращает
memoryviewобласти памяти, лежащей в основе этого буфера. Возвращаемый объект представляет собой одномерный, непрерывный по порядку C буфер памяти с форматомB(беззнаковые байты). Если буфер не непрерывен по порядку C или Fortran, вызываетсяBufferError.
-
release() -
Освобождает буфер, представленный объектом PickleBuffer.
-
Что можно сериализовать и десериализовать?
Можно сериализовать следующие типы:
-
None,True, иFalse; - целые, вещественные и комплексные числа;
- строки, байты, массивы байтов;
- кортежи, списки, множества и словари, содержащие только сериализуемые объекты;
- функции (встроенные и определённые пользователем), определённые на верхнем уровне модуля (с помощью
def, а неlambda); - классы, определённые на верхнем уровне модуля;
- экземпляры таких классов, чей
__dict__или результат вызова__getstate__()сериализуем (подробности см. в разделе Сериализация экземпляров класса).
Попытки сериализовать несериализуемые объекты вызовут исключение PicklingError; в этом случае может быть записано неопределённое количество байтов в файл. Попытка сериализации сильно рекурсивной структуры данных может превысить максимальную глубину рекурсии; в этом случае будет выброшено исключение RecursionError. Вы можете осторожно увеличить этот предел с помощью sys.setrecursionlimit().
Обратите внимание, что функции (встроенные и определённые пользователем) сериализуются по полному квалифицированному имени, а не по значению. 2 Это означает, что сериализуется только имя функции, а также имя модуля, в котором функция определена. Модуль определения должен быть импортируем в среде десериализации, и модуль должен содержать именованный объект; в противном случае будет выброшено исключение. 3
Аналогично, классы сериализуются по полному квалифицированному имени, поэтому в среде десериализации применяются те же ограничения. Обратите внимание, что код и данные класса не сериализуются вместе с ним; сериализуются только данные экземпляра. Это сделано намеренно, чтобы вы могли исправлять ошибки в классе или добавлять методы к классу и по-прежнему загружать объекты, созданные с более ранней версией класса. Если вы планируете иметь долгоживущие объекты, которые будут видеть много версий класса, может быть целесообразно поместить номер версии в объекты, чтобы класс мог выполнить соответствующие преобразования с помощью метода __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,
__getnewargs__()вызывался вместо__getnewargs_ex__()в протоколах 2 и 3.
-
object.__getstate__() -
Классы могут дополнительно влиять на то, как сериализуются их экземпляры; если класс определяет метод
__getstate__(), он вызывается, и возвращаемый объект сериализуется как содержимое экземпляра, вместо содержимого словаря экземпляра. Если метод__getstate__()отсутствует, то словарь экземпляра__dict__сериализуется в обычном режиме.
-
object.__setstate__(state) -
При десериализации, если класс определяет
__setstate__(), он вызывается со состоянием, десериализованным из данных. В этом случае нет требования, чтобы объект состояния был словарем. В противном случае, сериализованное состояние должно быть словарем, а его элементы присваиваются словарю нового экземпляра.Примечание
Если
__getstate__()возвращает ложное значение, метод__setstate__()не будет вызван при десериализации.
Дополнительную информацию о том, как использовать методы __getstate__() и __setstate__(), см. в разделе Обработка состоятельных объектов.
Примечание
Во время десериализации некоторые методы, такие как __getattr__(), __getattribute__() или __setattr__(), могут быть вызваны для экземпляра. В случае, если эти методы полагаются на соблюдение внутреннего инварианта, тип должен реализовать __new__() для установления этого инварианта, так как __init__() не вызывается при десериализации экземпляра.
Как мы увидим, pickle не использует непосредственно описанные выше методы. На самом деле, эти методы являются частью протокола копирования, который реализует специальный метод __reduce__(). Протокол копирования предоставляет унифицированный интерфейс для извлечения данных, необходимых для сериализации и копирования объектов. 4
Хотя мощный, реализация __reduce__() непосредственно в ваших классах может быть подвержена ошибкам. По этой причине разработчики классов должны использовать высокоуровневый интерфейс (т.е. __getnewargs_ex__(), __getstate__() и __setstate__()) всякий раз, когда это возможно. Однако мы покажем случаи, когда использование __reduce__() является единственным вариантом или приводит к более эффективной сериализации, или и тому, и другому.
-
object.__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объекта. Если неNone, этот вызываемый объект будет иметь приоритет над методом__setstate__()объекта.Добавлено в версии 3.8: Необязательный шестой элемент кортежа,
(obj, state), был добавлен.
-
object.__reduce_ex__(protocol) -
В качестве альтернативы, может быть определён метод
__reduce_ex__(). Единственное отличие состоит в том, что этот метод должен принимать единственный целочисленный аргумент — версию протокола. При определении этого метода, pickle будет отдавать предпочтение ему перед методом__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 с данными вне зоны обслуживания
Ограничение глобальных переменных
По умолчанию десериализация импортирует любой класс или функцию, найденную в данных сериализации. Для многих приложений это недопустимое поведение, так как оно позволяет десериализатору импортировать и вызывать произвольный код. Просто представьте, что делает этот искусственно созданный поток данных сериализации при загрузке:
>>> 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)
В следующем примере читаются данные, закодированные с помощью pickle.
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 -
Высокопроизводительная сериализация встроенных типов.
Примечания
-
1 -
Не путайте это с модулем
marshal -
2 -
Вот почему функции
lambdaнельзя закодировать с помощью pickle: все функцииlambdaимеют одно и то же имя:<lambda>. -
3 -
Возникающее исключение, скорее всего, будет
ImportErrorилиAttributeError, но это может быть что-то другое. -
4 -
Модуль
copyиспользует этот протокол для операций копирования поверхностно и глубоко. -
5 -
Ограничение на символы алфавитно-цифрового типа вызвано тем, что идентификаторы в протоколе 0 разделяются символом новой строки. Поэтому если в идентификаторах присутствуют символы новой строки, то полученные закодированные данные станут нечитаемыми.
© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.9/library/pickle.html