pickle — Сериализация объектов Python
Модуль pickle реализует фундаментальный, но мощный алгоритм сериализации и десериализации структуры Python-объектов. «Заморозка» (pickling) — это процесс преобразования иерархии Python-объектов в поток байтов, а «разморозка» (unpickling) — обратная операция, при которой поток байтов преобразуется обратно в иерархию объектов. Заморозку (и разморозку) также называют сериализацией, маршаллированием, 1 или «уплощением», однако, чтобы избежать путаницы, здесь используются термины «заморозка» и «разморозка».
В данном документе описываются модуль pickle и модуль cPickle.
Предупреждение
Модуль pickle не является безопасным при работе с ошибочными или злонамеренно сконструированными данными. Никогда не размораживайте данные, полученные из ненадежного или неавторизованного источника.
1. Связь с другими модулями Python
Модуль pickle имеет оптимизированный аналог под названием cPickle. Как следует из его названия, cPickle написан на C, поэтому он может быть до 1000 раз быстрее, чем pickle. Однако он не поддерживает наследование классов Pickler() и Unpickler(), поскольку в cPickle они являются функциями, а не классами. Большинству приложений не требуется такая функциональность, и они могут извлечь выгоду из улучшенной производительности cPickle. Помимо этого, интерфейсы обоих модулей почти идентичны; общий интерфейс описан в этом руководстве, а различия указаны там, где это необходимо. В последующих обсуждениях мы используем термин «заморозка», чтобы обобщить модули pickle и cPickle.
Потоки данных, которые генерируют оба модуля, гарантированно взаимозаменяемы.
В Python есть более примитивный модуль сериализации под названием marshal, но в целом pickle всегда должен быть предпочтительным способом сериализации Python-объектов. marshal существует в основном для поддержки файлов Python .pyc.
Модуль pickle отличается от marshal по нескольким важным аспектам:
-
Модуль
pickleотслеживает объекты, которые он уже сериализовал, таким образом, повторные ссылки на тот же объект не будут сериализованы повторно.marshalэтого не делает.Это имеет последствия как для рекурсивных объектов, так и для совместного использования объектов. Рекурсивные объекты — это объекты, которые содержат ссылки на себя. marshal не обрабатывает их, и попытка маршаллирования рекурсивных объектов приведет к сбою интерпретатора Python. Обмен объектами происходит, когда в разных частях иерархии сериализуемых объектов есть несколько ссылок на один и тот же объект.
pickleсохраняет такие объекты только один раз и гарантирует, что все остальные ссылки указывают на главный экземпляр. Обмен объектами сохраняется, что может быть очень важно для изменяемых объектов. -
marshalнельзя использовать для сериализации пользовательских классов и их экземпляров.pickleможет прозрачно сохранять и восстанавливать экземпляры классов, однако определение класса должно быть импортируемым и находиться в том же модуле, что и при хранении объекта. - Формат сериализации
marshalне гарантирует переносимость между версиями Python. Поскольку его основная задача — поддержка файлов.pyc, разработчики Python оставляют за собой право изменять формат сериализации несовместимым образом в случае необходимости. Формат сериализацииpickleгарантирует обратную совместимость между выпусками Python.
Обратите внимание, что сериализация — это более примитивное понятие, чем сохранение; хотя pickle читает и записывает файлы-объекты, он не решает вопрос именования постоянных объектов, а также (еще более сложный) вопрос одновременного доступа к постоянным объектам. Модуль pickle может преобразовать сложный объект в поток байтов и преобразовать поток байтов в объект с той же внутренней структурой. Возможно, самым очевидным действием с этими потоками байтов является их запись в файл, но также можно отправлять их по сети или хранить в базе данных. Модуль shelve предоставляет простой интерфейс для заморозки и разморозки объектов в файлах баз данных в стиле DBM.
2. Формат потока данных
Формат данных, используемый модулем pickle, специфичен для Python. Это дает преимущество отсутствия ограничений, накладываемых внешними стандартами, такими как XDR (который не может представлять обмен указателями); однако это означает, что программы, не написанные на Python, могут не быть в состоянии восстановить закодированные Python-объекты.
По умолчанию формат данных pickle использует удобочитаемое ASCII-представление. Это несколько объемнее, чем двоичное представление. Большое преимущество использования удобочитаемого ASCII (и некоторых других характеристик представления pickle) заключается в том, что для целей отладки или восстановления пользователь может просмотреть закодированный файл с помощью стандартного текстового редактора.
В настоящее время существует 3 разных протокола, которые могут использоваться для заморозки.
- Версия протокола 0 — это оригинальный ASCII-протокол и обратно совместима с более ранними версиями Python.
- Версия протокола 1 — это старый двоичный формат, который также совместим с более ранними версиями Python.
- Версия протокола 2 была введена в Python 2.3. Она обеспечивает гораздо более эффективную заморозку классов нового стиля.
Для получения дополнительной информации обратитесь к PEP 307.
Если протокол не указан, используется протокол 0. Если протокол указан как отрицательное значение или HIGHEST_PROTOCOL, будет использована самая последняя доступная версия протокола.
Изменено в версии 2.3: Введен параметр протокол.
Двоичный формат, который несколько более эффективен, может быть выбран путем указания версии протокола ≥ 1.
3. Использование
Чтобы сериализовать иерархию объектов, сначала создается заморозчик, а затем вызывается метод dump() заморозчика. Чтобы десериализовать поток данных, сначала создается разморозчик, а затем вызывается метод load() разморозчика. Модуль pickle предоставляет следующие константы:
-
pickle.HIGHEST_PROTOCOL -
Наивысшая версия протокола, доступная. Это значение может быть передано в качестве параметра protocol.
Новая функция в версии 2.3.
Примечание
Файлы pickle, созданные с протоколами >= 1, всегда следует открывать в двоичном режиме. Для старого протокола pickle 0, основанного на ASCII, можно использовать как текстовый, так и двоичный режим, при условии сохранения согласованности.
Файл pickle, записанный с протоколом 0 в двоичном режиме, будет содержать отдельные символы новой строки в качестве разделителей строк, и поэтому будет выглядеть «странно», когда просматривается в Блокноте или других редакторах, не поддерживающих этот формат.
Модуль pickle предоставляет следующие функции для повышения удобства процесса сериализации:
-
pickle.dump(obj, file[, protocol]) -
Записать сериализованное представление объекта obj в открытый файл file. Это эквивалентно
Pickler(file, protocol).dump(obj).Если параметр protocol опущен, используется протокол 0. Если protocol задан как отрицательное значение или
HIGHEST_PROTOCOL, будет использована самая последняя версия протокола.Изменено в версии 2.3: Введён параметр protocol.
file должен иметь метод
write(), принимающий единственный строковый аргумент. Таким образом, это может быть объект файла, открытый для записи, объектStringIOили любой другой пользовательский объект, соответствующий этому интерфейсу.
-
pickle.load(file) -
Прочитать строку из открытого объекта файла file и интерпретировать её как поток данных pickle, восстанавливая и возвращая исходную иерархию объектов. Это эквивалентно
Unpickler(file).load().file должен иметь два метода: метод
read(), принимающий целочисленный аргумент, и методreadline(), не принимающий аргументов. Оба метода должны возвращать строку. Таким образом, file может быть объектом файла, открытым для чтения, объектомStringIOили любым другим пользовательским объектом, соответствующим этому интерфейсу.Функция автоматически определяет, был ли поток данных записан в двоичном режиме или нет.
-
pickle.dumps(obj[, protocol]) -
Возвращает сериализованное представление объекта в виде строки вместо записи его в файл.
Если параметр protocol опущен, используется протокол 0. Если protocol задан как отрицательное значение или
HIGHEST_PROTOCOL, будет использована самая последняя версия протокола.Изменено в версии 2.3: Добавлен параметр protocol.
-
pickle.loads(string) -
Прочитать иерархию сериализованных объектов из строки. Символы в строке после представления сериализованного объекта игнорируются.
Модуль pickle также определяет три исключения:
-
exception pickle.PickleError -
Базовый класс для других исключений, определённых ниже. Наследуется от
Exception.
-
exception pickle.PicklingError -
Это исключение возникает, когда несериализуемый объект передаётся методу
dump().
-
exception pickle.UnpicklingError -
Это исключение возникает, когда возникает проблема при десериализации объекта. Обратите внимание, что при десериализации могут возникать и другие исключения, включая (но не ограничиваясь ими)
AttributeError,EOFError,ImportErrorиIndexError.
Модуль pickle также экспортирует две вызываемые функции 2, Pickler и Unpickler:
-
class pickle.Pickler(file[, protocol]) -
Принимает объект, подобный файлу, в который он будет записывать поток данных pickle.
Если параметр protocol опущен, используется протокол 0. Если protocol задан как отрицательное значение или
HIGHEST_PROTOCOL, будет использована самая последняя версия протокола.Изменено в версии 2.3: Введён параметр protocol.
file должен иметь метод
write(), принимающий единственный строковый аргумент. Это может быть открытый файл, объектStringIOили любой другой пользовательский объект, соответствующий этому интерфейсу.Picklerобъекты определяют один (или два) публичных метода:-
dump(obj) -
Записать сериализованное представление объекта obj в открытый объект файла, указанный в конструкторе. Используется либо двоичный, либо ASCII формат в зависимости от значения аргумента protocol, переданного в конструктор.
-
clear_memo() -
Очищает «запоминающую» структуру сериализатора. Запоминающая структура хранит информацию о ранее встреченных объектах, что важно для сериализации общих или рекурсивных объектов по ссылке, а не по значению. Этот метод полезен при повторном использовании сериализаторов.
Примечание
До Python 2.3,
clear_memo()был доступен только для сериализаторов, созданных с помощьюcPickle. В модулеpickle, у сериализаторов есть переменная экземпляра, которая называетсяmemo, которая является словарем Python. Таким образом, чтобы очистить запоминающую структуру сериализатора модуляpickle, можно сделать следующее:mypickler.memo.clear()
Код, которому не требуется поддержка более старых версий Python, должен просто использовать
clear_memo().
-
Возможно выполнение нескольких вызовов метода dump() одного и того же экземпляра Pickler. Они должны соответствовать тому же количеству вызовов метода load() соответствующего экземпляра Unpickler. Если один и тот же объект сериализован с помощью нескольких вызовов dump(), все вызовы load() вернут ссылки на один и тот же объект. 3
Unpickler объекты определяются следующим образом:
-
class pickle.Unpickler(file) -
Это принимает объект, подобный файлу, из которого он будет читать поток данных пикла. Этот класс автоматически определяет, был ли поток данных записан в двоичном режиме или нет, поэтому ему не нужен флаг, как в
Picklerфабрике.file должен иметь два метода, метод
read(), который принимает целочисленный аргумент, и методreadline(), который не требует аргументов. Оба метода должны возвращать строку. Таким образом, file может быть объектом файла, открытым для чтения, объектомStringIOили любым другим пользовательским объектом, который соответствует этому интерфейсу.Unpicklerобъекты имеют один (или два) общедоступных метода:-
load() -
Прочитать представление сериализованного объекта из открытого объекта файла, заданного в конструкторе, и вернуть реконструированную иерархию объектов, указанную в нём.
Этот метод автоматически определяет, был ли поток данных записан в двоичном режиме или нет.
-
noload() -
Это аналогично
load(), за исключением того, что оно фактически не создаёт никаких объектов. Это полезно в первую очередь для поиска так называемых «постоянных идентификаторов», которые могут быть указаны в потоке данных пикла. Более подробные сведения см. в разделе Протокол пикла ниже.Примечание: метод
noload()в настоящее время доступен только дляUnpicklerобъектов, созданных с помощью модуляcPickle. МодульpickleUnpicklerне имеет методаnoload().
-
4. Что можно сериализовать и десериализовать?
Следующие типы можно сериализовать:
-
None,True, иFalse - целые числа, длинные целые числа, числа с плавающей точкой, комплексные числа
- обычные и Unicode строки
- кортежи, списки, множества и словари, содержащие только сериализуемые объекты
- функции, определённые в верхней части модуля
- встроенные функции, определённые в верхней части модуля
- классы, определённые в верхней части модуля
- экземпляры таких классов, чьё
__dict__или результат вызова__getstate__()сериализуемо (см. раздел Протокол пикла для получения подробной информации).
Попытки сериализовать несериализуемые объекты приведут к исключению PicklingError; в этом случае в базовый файл может быть уже записано неопределённое количество байтов. Попытка сериализовать сильно рекурсивную структуру данных может превысить максимальную глубину рекурсии, в этом случае будет поднято исключение RuntimeError. Вы можете осторожно увеличить это ограничение с помощью sys.setrecursionlimit().
Обратите внимание, что функции (встроенные и пользовательские) сериализуются по «полнофункциональной» ссылке на имя, а не по значению. Это означает, что сериализуется только имя функции, а также имя модуля, в котором функция определена. Не сериализуются ни код функции, ни её атрибуты. Таким образом, определяющий модуль должен быть импортируемым в среде десериализации, и модуль должен содержать именованный объект, в противном случае будет поднято исключение. 4
Аналогично, классы сериализуются по именованной ссылке, поэтому в среде десериализации применяются те же ограничения. Обратите внимание, что код и данные класса не сериализуются, поэтому в следующем примере атрибут класса attr не восстанавливается в среде десериализации:
class Foo:
attr = 'a class attr'
picklestring = pickle.dumps(Foo)
Эти ограничения являются причиной того, что сериализуемые функции и классы должны быть определены в верхней части модуля.
Аналогично, когда экземпляры классов сериализуются, код и данные их класса не сериализуются вместе с ними. Сериализуются только данные экземпляра. Это сделано намеренно, чтобы вы могли исправлять ошибки в классе или добавлять методы к классу и всё равно загружать объекты, созданные с более ранней версией класса. Если вы планируете иметь долгоживущие объекты, которые будут видеть множество версий класса, может быть полезно поместить номер версии в объекты, чтобы класс мог выполнить соответствующие преобразования с помощью метода __setstate__().
5. Протокол пикла
В этом разделе описывается «протокол пикла», который определяет интерфейс между сериализатором/десериализатором и объектами, которые сериализуются. Этот протокол предоставляет стандартный способ определения, настройки и управления тем, как ваши объекты сериализуются и десериализуются. Описание в этом разделе не охватывает конкретные настройки, которые вы можете использовать для повышения безопасности среды десериализации от потоков данных пикла с неуверенным происхождением; см. раздел Наследование Unpicklers для получения более подробной информации.
5.1. Сериализация и десериализация обычных экземпляров классов
-
object.__getinitargs__() -
При десериализации экземпляра класса пикла его метод
__init__()обычно не вызывается. Если желательно, чтобы метод__init__()был вызван при десериализации, класс старого стиля может определить метод__getinitargs__(), который должен возвращать кортеж позиционных аргументов, которые должны быть переданы в конструктор класса (__init__(), например). Ключевые аргументы не поддерживаются. Метод__getinitargs__()вызывается во время сериализации; возвращаемый им кортеж включается в пикл для экземпляра.
-
object.__getnewargs__() -
Типы нового стиля могут предоставить метод
__getnewargs__(), который используется для протокола 2. Реализация этого метода необходима, если тип устанавливает внутренние инварианты при создании экземпляра или если выделение памяти зависит от значений, передаваемых методу__new__()для типа (как это происходит для кортежей и строк). Экземпляры класса нового стиляCсоздаются следующим образом:obj = C.__new__(C, *args)
где args — результат вызова
__getnewargs__()на исходном объекте; если нет__getnewargs__(), предполагается пустой кортеж.
-
object.__getstate__() -
Классы могут ещё больше влиять на то, как сериализуются их экземпляры; если класс определяет метод
__getstate__(), он вызывается, и состояние возврата сериализуется как содержимое для экземпляра вместо содержимого словаря экземпляра. Если нет метода__getstate__(), сериализуется__dict__экземпляра.
-
object.__setstate__(state) -
При распаковке, если класс также определяет метод
__setstate__(), он вызывается со значениями распакованного состояния. 5 Если метод__setstate__()отсутствует, распакованное состояние должно быть словарем, а его элементы присваиваются новому экземпляру. Если класс определяет оба метода__getstate__()и__setstate__(), объект состояния не обязательно должен быть словарем, и эти методы могут делать всё, что им нужно. 6Примечание
Для классов нового стиля, если
__getstate__()возвращает ложное значение, метод__setstate__()не будет вызван.
Примечание
При распаковке некоторые методы, такие как __getattr__(), __getattribute__() или __setattr__(), могут быть вызваны для экземпляра. В случае, если эти методы полагаются на то, что какое-то внутреннее инвариантное свойство является истинным, тип должен реализовывать либо метод __getinitargs__(), либо __getnewargs__(), чтобы установить такой инвариант; в противном случае ни __new__(), ни __init__() не будут вызваны.
5.2. Сериализация и десериализация типов расширений
-
object.__reduce__() -
Когда модуль
Picklerсталкивается с объектом типа, о котором он ничего не знает — например, с типом расширения — он ищет в двух местах подсказку, как его сериализовать. Одним из вариантов является реализация метода__reduce__()объектом. Если метод предоставлен, при сериализации будет вызван метод__reduce__()без аргументов, и он должен вернуть либо строку, либо кортеж.Если возвращается строка, она указывает на глобальную переменную, содержимое которой сериализуется в обычном режиме. Строка, возвращаемая методом
__reduce__(), должна быть локальным именем объекта относительно его модуля; модуль pickle ищет объект в пространстве имен модуля, чтобы определить модуль объекта.Когда возвращается кортеж, его длина должна быть от двух до пяти элементов. Дополнительные элементы могут быть опущены или значением
Noneможет быть установлено значение `None`. Содержимое кортежа сериализуется в обычном режиме и используется для восстановления объекта при десериализации. Семантика каждого элемента:-
Вызываемый объект, который будет вызван для создания начальной версии объекта. Следующий элемент кортежа предоставит аргументы для этого вызываемого объекта, а последующие элементы предоставят дополнительную информацию о состоянии, которая впоследствии будет использована для полного восстановления сериализованных данных.
В среде десериализации этот объект должен быть либо классом, либо вызываемым объектом, зарегистрированным как «безопасный конструктор» (см. ниже), или у него должен быть атрибут
__safe_for_unpickling__со значением `True`. В противном случае в среде десериализации будет вызвано исключениеUnpicklingError. Обратите внимание, что вызываемый объект, как обычно, сериализуется по имени. -
Кортеж аргументов для вызываемого объекта.
Изменено в версии 2.5: Ранее этот аргумент также мог быть
None. - Необязательно, состояние объекта, которое будет передано методу объекта
__setstate__(), как описано в разделе Сериализация и десериализация обычных экземпляров классов. Если у объекта нет метода__setstate__(), то, как и выше, значение должно быть словарем, и оно будет добавлено в словарь объекта__dict__. - Необязательно, итератор (а не последовательность), возвращающий последовательные элементы списка. Эти элементы списка будут сериализованы и добавлены к объекту, используя либо
obj.append(item)илиobj.extend(list_of_items). Это в основном используется для подклассов списков, но может использоваться и другими классами, если у них есть методыappend()иextend()с соответствующей сигнатурой. (Используется лиappend()илиextend()зависит от версии протокола pickle, а также от количества добавляемых элементов, поэтому оба варианта должны поддерживаться.) - Необязательно, итератор (а не последовательность), возвращающий последовательные элементы словаря, которые должны быть кортежами вида
(key, value). Эти элементы будут сериализованы и сохранены в объекте, используяobj[key] = value. Это в основном используется для подклассов словарей, но может использоваться и другими классами, если они реализуют__setitem__().
-
-
object.__reduce_ex__(protocol) -
Иногда полезно знать версию протокола при реализации метода
__reduce__(). Это можно сделать, реализовав метод с именем__reduce_ex__()вместо__reduce__(). Метод__reduce_ex__(), если он существует, вызывается вместо метода__reduce__()(вы по-прежнему можете предоставить метод__reduce__()для обратной совместимости). Метод__reduce_ex__()будет вызван с одним целочисленным аргументом — версией протокола.Класс
objectреализует методы__reduce__()и__reduce_ex__(); однако, если подкласс переопределяет метод__reduce__(), но не__reduce_ex__(), то реализация метода__reduce_ex__()обнаруживает это и вызывает метод__reduce__().
Альтернативой реализации метода __reduce__() для сериализуемого объекта является регистрация вызываемого объекта в модуле copy_reg. Этот модуль предоставляет способ для программ регистрировать «функции редукции» и конструкторы для пользовательских типов. Функции редукции имеют ту же семантику и интерфейс, что и метод __reduce__(), описанный выше, за исключением того, что они вызываются с одним аргументом — сериализуемым объектом.
Зарегистрированный конструктор считается «безопасным конструктором» для целей десериализации, как описано выше.
5.3. Сериализация и десериализация внешних объектов
Для обеспечения сохранения объектов модуль pickle поддерживает понятие ссылки на объект, находящийся за пределами потока сериализованных данных. Такие объекты ссылаются с помощью «постоянного идентификатора», который представляет собой произвольную строку удобочитаемых символов ASCII. Разрешение таких имён не определено модулем pickle; он делегирует это разрешение функциям, определённым пользователем, в сериализаторе и десериализаторе. 7
Для определения разрешения внешних постоянных идентификаторов необходимо установить атрибут persistent_id объекта сериализатора и атрибут persistent_load объекта десериализатора.
Для сериализации объектов с внешним постоянным идентификатором сериализатор должен иметь специальный метод persistent_id(), который принимает объект в качестве аргумента и возвращает либо None, либо постоянный идентификатор для этого объекта. Когда возвращается None, сериализатор просто сериализует объект в обычном режиме. Когда возвращается строковый постоянный идентификатор, сериализатор сериализует эту строку вместе с маркером, чтобы десериализатор распознал строку как постоянный идентификатор.
Для десериализации внешних объектов десериализатор должен иметь пользовательскую функцию persistent_load(), которая принимает строку постоянного идентификатора и возвращает ссылаемый объект.
Вот пример, который может прояснить ситуацию:
import pickle
from cStringIO import StringIO
src = StringIO()
p = pickle.Pickler(src)
def persistent_id(obj):
if hasattr(obj, 'x'):
return 'the value %d' % obj.x
else:
return None
p.persistent_id = persistent_id
class Integer:
def __init__(self, x):
self.x = x
def __str__(self):
return 'My name is integer %d' % self.x
i = Integer(7)
print i
p.dump(i)
datastream = src.getvalue()
print repr(datastream)
dst = StringIO(datastream)
up = pickle.Unpickler(dst)
class FancyInteger(Integer):
def __str__(self):
return 'I am the integer %d' % self.x
def persistent_load(persid):
if persid.startswith('the value '):
value = int(persid.split()[2])
return FancyInteger(value)
else:
raise pickle.UnpicklingError, 'Invalid persistent id'
up.persistent_load = persistent_load
j = up.load()
print j
В модуле cPickle атрибут unpickler’s persistent_load также может быть установлен в список Python. В этом случае, когда unpickler достигает постоянного идентификатора, строка постоянного идентификатора просто добавляется в этот список. Эта функциональность существует для того, чтобы поток данных pickle можно было «просканировать» на предмет ссылок на объекты, не фактически создавая все объекты в pickle. 8 Установка persistent_load в список обычно используется в сочетании с методом noload() объекта Unpickler.
6. Наследование Unpicklers
По умолчанию, при разархивировании (unpickling) импортируется любой класс, который обнаруживается в данных pickle. Вы можете контролировать то, что разархивируется и вызывается, настраивая свой unpickler. К сожалению, способ сделать это отличается в зависимости от того, используете ли вы pickle или cPickle. 9
В модуле pickle необходимо создать подкласс от Unpickler, переопределив метод load_global(). load_global() должен читать две строки из потока данных pickle, где первая строка будет именем модуля, содержащего класс, а вторая строка будет именем класса экземпляра. Затем он ищет класс, возможно, импортируя модуль и извлекая атрибут, а затем добавляет найденное в стек unpickler. Позже этот класс будет присвоен атрибуту __class__ пустого класса, как способ магического создания экземпляра без вызова метода __init__() класса. Ваша задача (если вы решите ее принять) — заставить load_global() поместить в стек unpickler безопасную версию любого класса, который вы считаете безопасным для разархивирования. Вам нужно создать такой класс самостоятельно. Или вы можете вызвать ошибку, если хотите запретить разархивирование всех экземпляров. Если это звучит как хак, то вы правы. Обратитесь к исходному коду, чтобы это работало.
В cPickle все немного чище, но ненамного. Чтобы контролировать, что разархивируется, можно установить атрибут unpickler’s find_global в функцию или None. Если это None, то любые попытки разархивировать экземпляры вызовут UnpicklingError. Если это функция, то она должна принимать имя модуля и имя класса и возвращать соответствующий объект класса. Она отвечает за поиск класса и выполнение необходимых импортов, и она может вызвать ошибку, чтобы предотвратить разархивирование экземпляров класса.
Вывод заключается в том, что вы должны быть очень осторожны с источником строк, которые разархивирует ваше приложение.
7. Пример
Для самого простого кода используйте функции dump() и load(). Обратите внимание, что самоссылочный список разархивируется и восстанавливается правильно.
import pickle
data1 = {'a': [1, 2.0, 3, 4+6j],
'b': ('string', u'Unicode string'),
'c': None}
selfref_list = [1, 2, 3]
selfref_list.append(selfref_list)
output = open('data.pkl', 'wb')
# Pickle dictionary using protocol 0.
pickle.dump(data1, output)
# Pickle the list using the highest protocol available.
pickle.dump(selfref_list, output, -1)
output.close()
Следующий пример показывает, как читать полученные разархивированные данные. При чтении файла, содержащего pickle, вы должны открыть файл в двоичном режиме, потому что вы не можете быть уверены, использовался ли ASCII или двоичный формат.
import pprint, pickle
pkl_file = open('data.pkl', 'rb')
data1 = pickle.load(pkl_file)
pprint.pprint(data1)
data2 = pickle.load(pkl_file)
pprint.pprint(data2)
pkl_file.close()
Вот более крупный пример, который показывает, как изменить поведение разархивирования для класса. Класс TextReader открывает текстовый файл и возвращает номер строки и содержимое строки каждый раз, когда вызывается его метод readline(). Если экземпляр TextReader разархивируется, все атрибуты, кроме члена объекта файла, сохраняются. При разархивировании экземпляра файл открывается заново, и чтение возобновляется с последней позиции. Методы __setstate__() и __getstate__() используются для реализации этого поведения.
#!/usr/local/bin/python
class TextReader:
"""Print and number lines in a text file."""
def __init__(self, file):
self.file = file
self.fh = open(file)
self.lineno = 0
def readline(self):
self.lineno = self.lineno + 1
line = self.fh.readline()
if not line:
return None
if line.endswith("\n"):
line = line[:-1]
return "%d: %s" % (self.lineno, line)
def __getstate__(self):
odict = self.__dict__.copy() # copy the dict since we change it
del odict['fh'] # remove filehandle entry
return odict
def __setstate__(self, dict):
fh = open(dict['file']) # reopen file
count = dict['lineno'] # read from file...
while count: # until line count is restored
fh.readline()
count = count - 1
self.__dict__.update(dict) # update attributes
self.fh = fh # save the file object
Пример использования может быть таким:
>>> import TextReader
>>> obj = TextReader.TextReader("TextReader.py")
>>> obj.readline()
'1: #!/usr/local/bin/python'
>>> obj.readline()
'2: '
>>> obj.readline()
'3: class TextReader:'
>>> import pickle
>>> pickle.dump(obj, open('save.p', 'wb'))
Если вы хотите увидеть, что pickle работает между процессами Python, запустите другую сессию Python, прежде чем продолжить. Далее может произойти как в том же процессе, так и в новом процессе.
>>> import pickle
>>> reader = pickle.load(open('save.p', 'rb'))
>>> reader.readline()
'4: """Print and number lines in a text file."""'
См. также
cPickle — Более быстрый pickle
Модуль cPickle поддерживает сериализацию и десериализацию объектов Python, предоставляя интерфейс и функциональность, почти идентичные модулю pickle. Есть несколько отличий, самое важное из которых — производительность и наследование.
Во-первых, cPickle может быть до 1000 раз быстрее, чем pickle, потому что первый реализован на C. Во-вторых, в модуле cPickle вызываемые Pickler() и Unpickler() являются функциями, а не классами. Это означает, что вы не можете использовать их для создания пользовательских подклассов для сериализации и десериализации. Большинству приложений эта функциональность не нужна и они должны получить выгоду от значительно улучшенной производительности модуля cPickle.
Потоки данных pickle, созданные модулями pickle и cPickle, идентичны, поэтому можно использовать pickle и cPickle взаимозаменяемо с существующими pickle. 10
Существуют дополнительные незначительные различия в API между cPickle и pickle, но для большинства приложений они взаимозаменяемы. Более подробная документация предоставлена в документации модуля pickle, которая включает список задокументированных различий.
Примечания
-
1 -
Не путайте это с модулем
marshal -
2 -
В модуле
pickleэти вызываемые элементы являются классами, которые можно наследовать, чтобы настроить поведение. Однако в модулеcPickleэти вызываемые элементы являются функциями-фабриками и поэтому не могут быть унаследованы. Одна из распространённых причин наследования — управление тем, какие объекты могут быть действительно распакованы. Дополнительные сведения см. в разделе Наследование распаковщиков. -
3 -
Предупреждение: это предназначено для сериализации нескольких объектов без промежуточных изменений в самих объектах или их частях. Если вы изменяете объект, а затем сериализуете его повторно с помощью того же экземпляра
Pickler, объект не будет сериализован повторно — сериализуется ссылка на него, иUnpicklerвернёт старое значение, а не изменённое. Здесь есть две проблемы: (1) обнаружение изменений и (2) сериализация минимального набора изменений. Здесь также может возникнуть проблема с сборкой мусора. -
4 -
Возникающее исключение, скорее всего, будет
ImportErrorилиAttributeError, но это может быть и что-то другое. -
5 -
Эти методы также можно использовать для реализации копирования экземпляров классов.
-
6 -
Этот протокол также используется операциями поверхностного и глубокого копирования, определёнными в модуле
copy. -
7 -
Фактический механизм сопоставления этих определённых пользователем функций немного отличается для
pickleиcPickle. Описание, приведённое здесь, работает одинаково для обеих реализаций. Пользователи модуляpickleтакже могли бы использовать наследование для достижения тех же результатов, переопределив методыpersistent_id()иpersistent_load()в производных классах. -
8 -
Мы оставим вас с изображением Гвидо и Джима, сидящих в своих гостиных, нюхающих маринованные огурцы.
-
9 -
Предупреждение: описанные здесь механизмы используют внутренние атрибуты и методы, которые могут измениться в будущих версиях Python. Мы намерены в будущем предоставить общий интерфейс для управления этим поведением, который будет работать как в
pickle, так и вcPickle. -
10 -
Поскольку формат данных pickle фактически представляет собой крошечный стековый язык программирования, и при кодировании некоторых объектов допускается определённая свобода, возможно, что два модуля создадут разные потоки данных для одних и тех же входных объектов. Однако гарантируется, что они всегда смогут читать потоки данных друг друга.
© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/2.7/library/pickle.html