pickle — Сериализация объектов Python

Исходный код: Lib/pickle.py


Модуль pickle реализует двоичные протоколы для сериализации и десериализации структуры объекта Python. «Пиклинг» - это процесс, посредством которого иерархия объекта Python преобразуется в поток байтов, а «анпиклинг» - обратная операция, посредством которой поток байтов (из двоичного файла или байтоподобного объекта) преобразуется обратно в иерархию объектов. Пиклинг (и анпиклинг) альтернативно известно как «сериализация», «маршаллинг», [1] или «распрямление»; однако, чтобы избежать путаницы, термины используемый здесь являются «пиклинг» (pickling) и «анпиклинг» (unpickling).

Предупреждение

Модуль pickle не безопасен. Unpickle только те данные, которым вы доверяете.

Существует возможность создания вредоносных pickle данных, которые будут выполнить произвольный код во время анпиклинга. Никогда не отцепляйте данные, которые могли быть получены из ненадежного источника, или которые могли быть подделаны.

Рекомендуется подписывать данные с помощью hmac, если необходимо убедиться в том, что они не были изменены.

Более безопасные форматы сериализации, такие как json, могут быть более подходящими при обработке ненадежных данных. Смотри Сравнение с json.

Связь с другими модулями Python

Сравнение с marshal

Python имеет более примитивный модуль сериализации, называемый marshal, но в целом pickle всегда должен быть предпочтительным способом сериализации Python объектов. marshal существует главным образом для поддержки файлов Python’а .pyc.

Модуль pickle отличается от marshal несколькими значимыми способами:

  • Модуль pickle отслеживает уже сериализованные объекты, поэтому последующие ссылки на тот же объект больше не будут сериализованы. marshal этого не делает.

    Это имеет последствия как для рекурсивных объектов, так и для общего доступа к объектам. Рекурсивные объекты - это объекты, содержащие ссылки на себя. Они не обрабатываются маршалом, и фактически попытка маршала рекурсивных объектов приведет к сбою вашего Python интерпретатор. Совместное использование объектов происходит при наличии нескольких ссылок на один и тот же объект в различных местах сериализуемой иерархии объектов. pickle сохраняет такие объекты только один раз и гарантирует, что все остальные ссылки указывают на главную копию. Общие объекты остаются общими, что может быть очень важно для изменчивых объектов.

  • marshal не может быть используемый, чтобы преобразовать в последовательную форму определенные пользователями классы и их сущности. pickle может сохранять и восстанавливать класс сущности прозрачно, однако определение класса должно быть импортируемым и существовать в том же модуле, что и при сохранении объекта.

  • Формат сериализации marshal не гарантирован как переносимый в разных версиях Python. Поскольку его основной задачей в жизни является поддержка файлов .pyc, исполнители Python оставляют за собой право изменять формат сериализации не обратно совместимыми способами в случае возникновения необходимости. Формат преобразования в последовательную форму pickle, как гарантируют, будет назад совместим через выпуски Python, если совместимый протокол pickle выбран и пиклинг и не pickle соглашения код с Python 2 к различиям в типе Python 3, если ваши данные пересекают ту уникальную языковую границу изменения ломки.

Сравнение с json

Существуют принципиальные различия между протоколами pickle и JSON (JavaScript Object Notation):

  • JSON является форматом сериализации текста (он выводит текст в Юникоде, хотя большую часть времени он затем кодированный в utf-8), в то время как pickle является двоичным форматом сериализации;
  • JSON является читаемым человеком, в то время как pickle не является;
  • JSON является совместимым и широко используемый за пределами экосистемы Python, в то время как pickle является Python-specific;
  • По умолчанию JSON может представлять только подмножество встроенных типов Python без пользовательских классов; pickle может представлять чрезвычайно большое количество типов Python (многие из них автоматически, умным использованием объектов самоанализа Python’а; сложные случаи могут быть решены путем реализации API конкретных объектов);
  • В отличие от pickle, десериализовывая JSON, которому не доверяют, сам по себе не создает произвольную уязвимость выполнения код.

См.также

Модуль json: стандартный библиотечный модуль, позволяющий выполнять сериализацию и десериализацию JSON.

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

Формат данных используемый по pickle является Python-specific. Это имеет то преимущество, что не существует ограничений, налагаемых внешними стандартами, такими как JSON или XDR (которые не могут представлять совместное использование указателей); однако это означает, что программы не-Python могут быть не в состоянии реконструировать объекты pickled Python.

По умолчанию формат pickle данных использует относительно компактное двоичное представление. Если вам нужны оптимальные особенности размера, вы можете эффективно данные о compress pickled.

Модуль pickletools содержит инструменты для анализа потоков данных, генерируемых pickle. pickletools источник код имеет обширные комментарии о опкоды используемый по протоколам pickle.

Есть в настоящее время 6 различных протоколов, которые могут быть используемый для пиклинг. Чем выше протокол используемый, тем более поздняя версия Python необходима для чтения произведенных pickle.

  • Протокол версии 0 является исходным «читаемым человеком» протоколом и обратно совместим с более ранними версиями Python.
  • Протокол версии 1 является старым двоичным форматом, который также совместим с более ранними версиями Python.
  • Протокол версии 2 был представлен в Python 2.3. Это обеспечивает намного более эффективный пиклинг классов нового стиля. Информацию об улучшениях протокола 2 см. в разделе PEP 307.
  • Версия протокола 3 добавлена в Python 3.0. Это имеет явную поддержку объектов bytes и не может быть unpickled 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 обеспечивает простой интерфейс для pickle и открепления объектов в файлах базы данных в стиле 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)

Записать представление pickled объекта 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)

Возвращает представление pickled объекта obj как bytes возражают, вместо того, чтобы писать это файлу.

Аргументы protocol, fix_imports и buffer_callback имеют то же значение, что и в конструкторе Pickler.

Изменено в версии 3.8: Добавлен аргумент buffer_callback.

pickle.load(file, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)

Прочитайте представление pickled объекта от открытого файлового объекта file и возвращает воссозданная иерархия объекта, определенная там. Это эквивалентно Unpickler(file).load().

Версия протокола pickle обнаруживается автоматически, поэтому аргумент протокола не требуется. Байты за pickled представлением объекта игнорируются.

У аргументов file, fix_imports, encoding, errors, strict и buffers есть то же значение как в конструкторе Unpickler.

Изменено в версии 3.8: Добавлен аргумент buffers.

pickle.loads(data, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)

Возвращает воссозданную иерархию объекта представления pickled data объекта. data должен быть байтоподобным объектом.

Версия протокола pickle обнаруживается автоматически, поэтому аргумент протокола не требуется. Байты за pickled представлением объекта игнорируются.

У аргументов file, fix_imports, encoding, errors, strict и buffers есть то же значение как в конструкторе Unpickler.

Изменено в версии 3.8: Добавлен аргумент buffers.

Модуль pickle определяет три исключения:

exception pickle.PickleError

Общий базовый класс для других исключений пиклинг. Он наследует Exception.

exception pickle.PicklingError

Ошибка подняла, когда с unpicklable объектом сталкивается Pickler. Он наследует PickleError.

Обратитесь к Что может быть pickled и unpickled?, чтобы изучить, какие виды объектов могут быть pickled.

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 (), который принимает один аргумент в байтах. Таким образом, это может быть файл на диске, открытый для двоичной записи, 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)

Напишите представление pickled obj к открытому объекту файла, данному в конструкторе.

persistent_id(obj)

По умолчанию ничего не делать. Это существует, чтобы подкласс мог переопределить его.

Если persistent_id() возвращает None, obj - pickled, как обычно. Любой другой значение заставляет Pickler испускать возвращенный значение как настойчивый ID для obj. Значение этого постоянного идентификатора должно быть определено параметром Unpickler.persistent_load(). Обратите внимание, что значение возвращенный by persistent_id() не может иметь постоянный идентификатор.

Подробности и примеры использования см. в разделе Сохранение внешних объектов.

dispatch_table

Диспетчерская таблица объекта Pickler представляет собой реестр редукционных функций такого рода, который может быть объявлен с помощью copyreg.pickle(). Это мэппинг, ключами которого являются классы, а значения - функции сокращения. Функция сокращения берет единственный аргумент связанного класса и должна соответствовать тому же интерфейсу как метод __reduce__().

По умолчанию объект pickler не будет иметь dispatch_table атрибут и вместо этого будет использовать глобальную таблицу диспетчеризации, управляемую модулем copyreg. Однако для настройки пиклинг для определенного объекта пиклера можно задать dispatch_table атрибут для объекта словареподобный. С другой стороны, если у подкласс Pickler будет dispatch_table атрибут тогда, то это будет используемый как таблицей отправки по умолчанию для сущности того класса.

Примеры использования см. в разделе Таблицы диспетчеризации.

Добавлено в версии 3.3.

reducer_override(self, obj)

Специальный редуктор, который может быть определен в Pickler подклассы. Этот метод имеет приоритет над любым редуктором в dispatch_table. Это должно соответствовать тому же интерфейсу как метод __reduce__(), и может произвольно возвращает NotImplemented к отступлению на dispatch_table-зарегистрированных преобразователях данных к pickle obj.

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

Добавлено в версии 3.8.

fast

Запрещен. Включить быстрый режим, если установлено в истинный значение. Быстрый режим отключает использование записки, поэтому ускорение процесса пиклинг, не производя лишний PUT опкоды. Это не должен быть используемый с самосправочными объектами, делание иначе заставит Pickler повторно проклинать бесконечно.

Используйте pickletools.optimize(), если вам нужны более компактные пикли (pickles).

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-разрядные строка сущности pickled на Python 2; по умолчанию для «ASCII» и «strict» соответственно. encoding может быть „байтами“, чтобы читать эти 8-битный строка сущности как объекты байтов. Использование encoding='latin1' требуется для открепления массивов NumPy и сущности datetime, date и time pickled на Python 2.

Если buffers имеет значение None (по умолчанию), то все данные, необходимые для десериализации, должны содержаться в потоке pickle. Это означает, что аргумент buffer_callback был None при создании экземпляра Pickler (или при вызове dump() или dumps()).

Если buffers не является None, он должен представлять собой итабль объектов с поддержкой буфера, который используется каждый раз, когда поток pickle ссылается на представление буфера из группы. Такие буферы даны для buffer_callback объекта пиклера.

Изменено в версии 3.8: Добавлен аргумент buffers.

load()

Прочитайте представление pickled объекта от открытого объекта файла, данного в конструкторе и возвращает воссозданная иерархия объекта, определенная там. Байты за pickled представлением объекта игнорируются.

persistent_load(pid)

По умолчанию поднимите UnpicklingError.

Если определено, persistent_load() должен возвращает объект, указанный постоянным идентификатором pid. Если с недействительным настойчивым ID сталкиваются, UnpicklingError должен быть поднят.

Подробности и примеры использования см. в разделе Сохранение внешних объектов.

find_class(module, name)

Импортировать module при необходимости и возвращает вызванный name из него объект, где аргументы module и name являются str объектами. Обратите внимание, в отличие от его названия предполагает, что find_class() также является используемый для поиска функций.

Подклассы могут переопределять это, чтобы получить контроль над тем, какой тип объектов и как они могут быть загружены, что потенциально снижает риски безопасности. Дополнительные сведения см. в разделе Ограничение Globals.

Raises an auditing event pickle.find_class with arguments module, name.

class pickle.PickleBuffer(buffer)

Оболочка для буфера, представляющая выбираемые данные. buffer должен быть buffer-providing объектом, таким как байтоподобный объект или N-мерный массив.

PickleBuffer сам является поставщиком буфера, поэтому его можно передать другим API, ожидающим объекта, предоставляющего буфер, например memoryview.

PickleBuffer объекты можно сериализовать только с помощью протокола pickle 5 или выше. Они имеют право на внеполосная сериализация.

Добавлено в версии 3.8.

raw()

Возвращает memoryview области памяти, лежащей в основе этого буфера. Объект возвращенный представляет собой одномерное C-непрерывное запоминающее представление с форматом B (байты без знака). BufferError поднимается, если буфер не является ни C, ни Fortran-смежным.

release()

Освободить базовый буфер, предоставленный объектом PickleBuffer.

Что может быть pickled и unpickled?

Следующие типы могут быть pickled:

  • None, True и False
  • целые числа, числа с плавающей запятой, комплексные числа
  • строки, байты, байтовые массивы
  • кортежи, списки, наборы и словари, содержащие только выбираемые объекты
  • функции, определенные на верхнем уровне модуля (с использованием def, а не lambda)
  • встроенные функции, определенные на верхнем уровне модуля
  • классы, определенные на верхнем уровне модуля
  • сущности таких классов, __dict__ которых или результат вызова __getstate__() является податливым (подробнее см. раздел Пиклинг класса сущности).

Попытки к объектам pickle unpicklable поднимут исключение PicklingError; когда это происходит, в базовый файл может быть уже записано неопределенное количество байт. Попытка к pickle, очень рекурсивная структура данных может превысить максимальную глубину рекурсии, RecursionError, будет поднята в этом случае. Вы можете осторожно повысить этот лимит с помощью sys.setrecursionlimit().

Обратите внимание, что функции (встроенный и определенный пользователями) являются pickled «полностью компетентной» ссылкой имени, не значение. [2] это означает, что pickled является только имя функции, а также имя модуля, в котором определена функция. Ни код функции, ни какая-либо ее функция атрибуты не являются pickled. Таким образом, определяющий модуль должен быть импортирован в среде отмены выбора, а модуль должен содержать именованный объект, в противном случае возникает исключение. [3]

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

class Foo:
    attr = 'A class attribute'

picklestring = pickle.dumps(Foo)

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

Аналогично, когда класс сущности является pickled, код и данные их класса не являются pickled вместе с ними. Только данные сущность - pickled. Это делается специально, чтобы можно было исправлять ошибки в классе или добавлять методы в класс и по-прежнему загружать объекты, созданные в более ранней версии класса. Если планируется наличие долгоживущих объектов, которые будут видеть множество версий класса, может оказаться целесообразным поместить номер версии в объекты, чтобы подходящие преобразования могли быть сделаны методом класса __setstate__().

Пиклинг класса сущности

В этом разделе мы описываем общие механизмы, доступные вам, чтобы определить, настроить, и управлять, как класс сущности - pickled и unpickled.

В большинстве случаев не требуется никакого дополнительного кода, чтобы сделать пиклабельные (picklable) сущности. По умолчанию pickle будет извлекать класс и атрибуты сущность с помощью самоанализа. Когда класс сущность - unpickled, его метод __init__() обычно - не призванный. Поведение по умолчанию сначала создает неинициализированный сущность, а затем восстанавливает сохраненный атрибуты. Следующий код показывает внедрение этого поведения:

def save(obj):
    return (obj.__class__, obj.__dict__)

def load(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__() класса требует ключевой-only аргументов. В противном случае рекомендуется для совместимости реализовать __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__()

Классы могут дополнительно влиять на то, как их сущности являются pickled; если класс определяет метод __getstate__(), это называют, и объект возвращенный - pickled как содержание для сущность вместо содержания словаря сущность. Если метод __getstate__() отсутствует, сущность __dict__ - pickled, как обычно.

object.__setstate__(state)

При откате, если класс определяет __setstate__(), он вызывается с unpickled состояние. В этом случае нет требования к тому, чтобы объект состояние был словарем. Иначе pickled состояние должен быть словарем, и его предметы назначены на словарь новой сущности.

Примечание

Если __getstate__() возвращает a false значение, метод __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 - используемый, а также количество предметов, чтобы приложить, таким образом, оба должны быть поддержаны.)

  • Необязательно, итератор (не последовательность), дающий последовательные пары key-значение. Эти элементы будут сохранены в объекте с помощью obj[key] = value. Это в первую очередь используемый для словаря подклассы, но может быть используемый другими классами, пока они реализуют __setitem__().

  • При необходимости вызываемый объект с (obj, state) сигнатура. Это подлежащее выкупу позволяет пользователю программно управлять поведением состояние-обновления конкретной цели, вместо того, чтобы использовать статический метод obj __setstate__(). Если не None, этот вызываемый абонент будет иметь приоритет над __setstate__() obj.

    Добавлено в версии 3.8: Добавлен дополнительный шестой элемент кортежа (obj, state).

object.__reduce_ex__(protocol)

Альтернативно, может быть определен метод __reduce_ex__(). Единственное отличие состоит в том, что этот метод должен принимать один целочисленный аргумент, версию протокола. После определения, pickle предпочтет метод __reduce__(). Кроме того, __reduce__() автоматически становится синонимом расширенной версии. Главное использование для этого метода должно обеспечить назад совместимый, уменьшают значения для более старых выпусков Python.

Сохранение внешних объектов

В интересах сохранения объекта модуль pickle поддерживает понятие ссылки на объект вне потока pickled данных. На такие объекты ссылается постоянный идентификатор, который должен быть либо строка буквенно-цифровых символов (для протокола 0) [5], либо просто произвольным объектом (для любого нового протокола).

Разрешение таких постоянных идентификаторов не определяется модулем pickle; он делегирует это разрешение определяемым пользователем методам на отборщике и распаковщике, persistent_id() и persistent_load() соответственно.

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

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

Вот всестороннее представление в качестве примера, как настойчивый ID может быть используемый к внешним объектам pickle ссылкой.

# 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, является:

copyreg.pickle(SomeClass, reduce_SomeClass)
f = io.BytesIO()
p = pickle.Pickler(f)

Обработка объектов с учетом состояния

Вот пример, который показывает, как изменить поведение пиклинг для класса. Класс TextReader открывает текстовый файл и возвращает номер строки и содержимое строки при каждом вызове метода readline(). Если TextReader сущность является pickled, все атрибуты кроме элемента объекта файла сохраняются. Когда сущность - unpickled, файл вновь открыт, и читающий возобновляет от последнего местоположения. Методы __setstate__() и __getstate__() являются используемый для реализации этого поведения:

class TextReader:
    """Печать и нумерация строк в текстовом файле."""

    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):
        # Скопировать объект состояние из self.__dict__, который содержит все нашей сущность
        # атрибуты. Всегда используйте dict.copy() метод, чтобы не изменять
        # оригинальный состояние.
        state = self.__dict__.copy()
        # Удалите недопустимые (unpicklable) записи.
        del state['file']
        return state

    def __setstate__(self, state):
        # Восстановить атрибуты сущности (то есть имя файла и lineno).
        self.__dict__.update(state)
        # Восстановите состояние ранее открытого файла. Для этого нам нужно снова открыть
        # его и читать с него до тех пор, пока не будет восстановлен счетчик строк.
        file = open(self.filename)
        for _ in range(self.lineno):
            file.readline()
        # Наконец, сохранить файл.
        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):
        """Пользовательский редюсер для MyClass."""
        if getattr(obj, "__name__", None) == "MyClass":
            return type, (obj.__name__, obj.__bases__,
                          {'my_attribute': obj.my_attribute})
        else:
            # Для любого другого объекта вернитесь к обычному сокращению
            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 и из него.

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

API поставщика

Большие объекты данных быть pickled должны осуществить метод __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 запрещен для протоколов pickle <= 4.
            return type(self)._reconstruct, (bytearray(self),)

    @classmethod
    def _reconstruct(cls, obj):
        with memoryview(obj) as m:
            # Получить обработчик над исходным буферным объектом
            obj = m.obj
            if type(obj) is cls:
                # Исходный буферный объект представляет собой ZeroCopyByteArray,
                # возвращается как есть.
                return obj
            else:
                return cls(obj)

reconstructor (метод класса _reconstruct) возвращает обеспечение буфера объекта, если у этого есть правильный тип. Это простой способ имитировать поведение нулевой копии на этом примере игрушки.

На стороне потребителя мы можем pickle эти объекты обычным способом, который при несериализации даст нам копию оригинального объекта:

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: копия была сделана

Но если мы передадим 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: копия не была сделана

Этот пример ограничен тем, что bytearray выделяет собственную память: нельзя создать bytearray сущность, поддерживаемую памятью другого объекта. Однако сторонние типы данных, такие как множества NumPy не имеют этого ограничения и позволяют использование нулевой копии пиклинг (или делающий как можно меньше копий), переходя между отличными процессами или системами.

См.также

PEP 574 - Pickle протокол 5 с данными из группы

Ограничение Globals

По умолчанию отмена выбора приведет к импорту любого класса или функции, найденных в pickle данных. Во многих случаях такое поведение неприемлемо, поскольку позволяет нарушителю импортировать и использовать произвольные код. Просто рассмотрите то, что это изготовило вручную поток данных pickle, делает, когда он загружен:

>>> import pickle
>>> pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
hello world
0

В этом примере unpickler импортирует функцию os.system(), и затем примените аргумент строка «эхо привет мир». Хотя этот пример не является оскорбительным, нетрудно представить тот, который может повредить вашу систему.

Поэтому вы можете хотеть управлять тем, что получает unpickled, настраивая 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):
        # Разрешить только безопасные классы от встроенных.
        if module == "builtins" and name in safe_builtins:
            return getattr(builtins, name)
        # Запретить все остальное.
        raise pickle.UnpicklingError("global '%s.%s' is forbidden" %
                                     (module, name))

def restricted_loads(s):
    """Вспомогательная функция аналогичная pickle.loads()."""
    return RestrictedUnpickler(io.BytesIO(s)).load()

Образец использования нашей работы unpickler предназначался:

>>> 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

Как показывают наши примеры, вы должны быть осторожны с тем, что вы позволяете быть unpickled. Поэтому, если безопасность является проблемой, может потребоваться рассмотреть альтернативные варианты, такие как API-интерфейс сортировки в решениях xmlrpc.client или сторонних производителей.

Исполнение

Последние версии протокола pickle (от протокола 2 и выше) имеют эффективные двоичные кодировки для нескольких общих функций и встроенных типов. Кроме того, модуль pickle имеет прозрачный оптимизатор, написанный на C.

Примеры

Для простейших код используйте функции dump() и load():

import pickle

# Произвольная коллекция объектов, поддерживаемых pickle.
data = {
    'a': [1, 2.0, 3, 4+6j],
    'b': ("character string", b"byte string"),
    'c': {None, True, False}
}

with open('data.pickle', 'wb') as f:
    # Pickle «словарь данных», используя самый высокий доступный протокол.
    pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)

В следующем примере считываются результирующие данные pickled:

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)

См.также

Module copyreg
Регистрация конструктора интерфейса Pickle для типов расширений.
Модуль pickletools
Инструменты для работы и анализа pickled данных.
Модуль shelve
Индексированные базы данных объектов; использует pickle.
Модуль copy
Поверхностное и глубокое копирование объектов.
Модуль marshal
Высокопроизводительная сериализация встроенных типов.

Сноски

[1]Не путайте с модулем marshal
[2]Вот почему функции lambda не могут быть pickled: все функции lambda имеют одно и то же имя: <lambda>.
[3]Вызванное исключение, вероятно, будет ImportError или AttributeError но это может быть что-то другое.
[4]Модуль copy использует этот протокол для операций поверхностного и глубокого копирования.
[5]Ограничение буквенно-цифровых символов обусловлено фактом постоянные идентификаторы в протоколе 0 разделяются новой строкой символ. Поэтому, если какой-либо вид newline знаков произойдет в настойчивых ID, то получающийся pickle станет нечитабельным.