Содержание страницы
11.1. pickle – сериализация объектов Python¶pickle – Python object serialization
Модуль pickle реализует фундаментальный, но мощный алгоритм для
сериализации и десериализации структуры объектов Python. «Пиклирование» – это
процесс, при котором иерархия объектов Python преобразуется в поток байтов, а
«распиклирование» – обратная операция, при которой поток байтов преобразуется обратно
в иерархию объектов. Пиклирование (и распиклирование) также известно как
«сериализация», «маршалинг», 1 или «уплощение», однако, чтобы избежать
путаницы, здесь используются термины «пиклирование» и «распиклирование».
Эта документация описывает как модуль pickle, так и модуль cPickle.
Предупреждение
Модуль pickle не защищён от ошибочных или вредоносных
данных. Никогда не десериализуйте данные, полученные из ненадёжного или
непроверенного источника.
11.1.1. Взаимосвязь с другими модулями Python¶Relationship to other Python modules
У модуля pickle есть оптимизированный собрат, называемый модулем cPickle. Как следует из названия, cPickle написан на C, поэтому он может быть до 1000 раз быстрее, чем pickle. Однако он не поддерживает наследование от классов Pickler() и Unpickler(), потому что в cPickle это функции, а не классы. Большинству приложений эта функциональность не нужна, и они могут выиграть от повышенной производительности cPickle. В остальном интерфейсы двух модулей почти идентичны; общий интерфейс описан в этом руководстве, а различия отмечены там, где это необходимо. В дальнейшем мы используем термин «pickle» для совместного описания модулей pickle и cPickle.
Потоки данных, производимые двумя модулями, гарантированно взаимозаменяемы.
В Python есть более примитивный модуль сериализации marshal, но в целом pickle всегда должен быть предпочтительным способом сериализации объектов Python. marshal существует в первую очередь для поддержки файлов .pyc в Python.
Модуль pickle отличается от marshal несколькими важными особенностями:
Модуль
pickleотслеживает уже сериализованные объекты, чтобы последующие ссылки на тот же объект не сериализовались повторно.marshalэтого не делает.This has implications both for recursive objects and object sharing. Recursive objects are objects that contain references to themselves. These are not handled by marshal, and in fact, attempting to marshal recursive objects will crash your Python interpreter. Object sharing happens when there are multiple references to the same object in different places in the object hierarchy being serialized.
picklestores such objects only once, and ensures that all other references point to the master copy. Shared objects remain shared, which can be very important for mutable objects.marshalнельзя использовать для сериализации пользовательских классов и их экземпляров.pickleможет прозрачно сохранять и восстанавливать экземпляры классов, однако определение класса должно быть импортируемо и находиться в том же модуле, что и при сохранении объекта.Формат сериализации
marshalне гарантирует переносимости между версиями Python. Поскольку его основная задача – поддержка файлов.pyc, разработчики Python оставляют за собой право изменять формат сериализации несовместимым с предыдущими версиями образом, если возникнет необходимость. Формат сериализацииpickleгарантирует обратную совместимость между выпусками Python.
Обратите внимание, что сериализация – это более примитивное понятие, чем персистентность; хотя pickle читает и записывает файловые объекты, он не решает проблему именования персистентных объектов, ни (ещё более сложную) проблему конкурентного доступа к персистентным объектам. Модуль pickle может преобразовать сложный объект в поток байтов и может преобразовать поток байтов обратно в объект с той же внутренней структурой. Вероятно, самое очевидное, что можно сделать с этими потоками байтов – записать их в файл, но также можно отправить их по сети или сохранить в базе данных. Модуль shelve предоставляет простой интерфейс для пиклирования и распиклирования объектов в файлах баз данных в стиле DBM.
11.1.2. Формат потока данных¶Data stream format
Формат данных, используемый pickle, является специфичным для Python. Это даёт преимущество: нет ограничений, налагаемых внешними стандартами, такими как XDR (который не может представлять разделение указателей); однако это означает, что программы не на Python могут не иметь возможности восстановить пиклированные объекты Python.
По умолчанию формат данных pickle использует печатное ASCII-представление. Это немного объёмнее, чем двоичное представление. Большим преимуществом использования печатного ASCII (и некоторых других характеристик представления pickle) является то, что для целей отладки или восстановления человек может прочитать пиклированный файл с помощью стандартного текстового редактора.
В настоящее время существует 3 различных протокола, которые можно использовать для пиклирования.
Версия протокола 0 является оригинальным ASCII-протоколом и обратно совместима с предыдущими версиями Python.
Версия протокола 1 – это старый двоичный формат, также совместимый с ранними версиями Python.
Версия протокола 2 была представлена в Python 2.3. Она обеспечивает гораздо более эффективную упаковку (pickling) классов нового стиля.
Смотрите PEP 307 для получения дополнительной информации.
Если протокол не указан, используется протокол 0. Если протокол указан как отрицательное значение или HIGHEST_PROTOCOL, будет использована самая высокая доступная версия протокола.
Изменено в версии 2.3: Добавлен параметр протокол.
Двоичный формат, который немного эффективнее, можно выбрать, указав протокол версии >= 1.
11.1.3. Использование¶Usage
Чтобы сериализовать иерархию объектов, сначала создаётся pickler, затем вызывается его метод dump(). Чтобы десериализовать поток данных, сначала создаётся unpickler, затем вызывается его метод load(). Модуль pickle предоставляет следующую константу:
-
pickle.HIGHEST_PROTOCOL¶ Самая высокая доступная версия протокола. Это значение можно передать как аргумент protocol.
Новое в версии 2.3.
Примечание
Файлы pickle, созданные с протоколами >= 1, следует всегда открывать в бинарном режиме. Для старого ASCII-протокола pickle 0 можно использовать как текстовый, так и бинарный режим, при условии соблюдения согласованности.
Файл pickle, записанный с протоколом 0 в бинарном режиме, будет содержать одиночные символы перевода строки в качестве разделителей строк и поэтому будет выглядеть «странно» при просмотре в Блокноте или других редакторах, не поддерживающих этот формат.
Модуль pickle предоставляет следующие функции для упрощения процесса упаковки:
-
pickle.dump(obj, file[, protocol])¶ Записывает пиклированное представление объекта obj в открытый файловый объект file. Это эквивалентно
Pickler(file, protocol).dump(obj).Если параметр протокол опущен, используется протокол 0. Если протокол указан как отрицательное значение или
HIGHEST_PROTOCOL, будет использована самая высокая версия протокола.Изменено в версии 2.3: Добавлен параметр протокол.
file должен иметь метод
write(), который принимает один строковый аргумент. Таким образом, это может быть файловый объект, открытый для записи, объектStringIOили любой другой пользовательский объект, соответствующий этому интерфейсу.
-
pickle.load(file)¶ Читает строку из открытого файлового объекта file и интерпретирует её как поток данных pickle, восстанавливая и возвращая исходную иерархию объектов. Это эквивалентно
Unpickler(file).load().file должен иметь два метода: метод
read(), принимающий целочисленный аргумент, и методreadline(), не требующий аргументов. Оба метода должны возвращать строку. Таким образом, file может быть файловым объектом, открытым для чтения, объектомStringIOили любым другим пользовательским объектом, соответствующим этому интерфейсу.Эта функция автоматически определяет, был ли поток данных записан в двоичном режиме или нет.
-
pickle.dumps(obj[, protocol])¶ Возвращает пиклированное представление объекта в виде строки, вместо записи в файл.
Если параметр протокол опущен, используется протокол 0. Если протокол указан как отрицательное значение или
HIGHEST_PROTOCOL, будет использована самая высокая версия протокола.Изменено в версии 2.3: Был добавлен параметр протокол.
-
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)¶ Записывает представление объекта в pickle в открытый файловый объект, переданный конструктору. Будет использован бинарный или ASCII-формат в зависимости от значения аргумента protocol, переданного конструктору.
-
clear_memo()¶ Очищает «memo» (память) пиклера. 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)¶ Принимает файлоподобный объект, из которого будет читать поток данных pickle. Этот класс автоматически определяет, был ли поток данных записан в бинарном режиме или нет, поэтому ему не нужен флаг, как в фабрике
Pickler.file должен иметь два метода: метод
read(), принимающий целочисленный аргумент, и методreadline(), не требующий аргументов. Оба метода должны возвращать строку. Таким образом, file может быть файловым объектом, открытым для чтения, объектомStringIOили любым другим пользовательским объектом, соответствующим этому интерфейсу.Объекты
Unpicklerимеют один (или два) открытых метода:-
load()¶ Читает представление объекта в pickle из открытого файлового объекта, переданного конструктору, и возвращает восстановленную иерархию объектов, указанную в нём.
Этот метод автоматически определяет, был ли поток данных записан в бинарном режиме или нет.
-
noload()¶ Этот метод похож на
load(), за исключением того, что он не создаёт реально никаких объектов. Это полезно в первую очередь для поиска так называемых «persistent ids» (постоянных идентификаторов), которые могут быть использованы в потоке данных pickle. См. раздел Протокол pickle ниже для получения дополнительных сведений.Примечание: метод
noload()в настоящее время доступен только для объектовUnpickler, созданных с помощью модуляcPickle. ОбъектыUnpicklerмодуляpickleне имеют методаnoload().
-
11.1.4. Что можно упаковать и распаковать с помощью pickle?¶What can be pickled and unpickled?
Следующие типы можно сериализовать:
None,TrueиFalseцелые числа, длинные целые числа, числа с плавающей запятой, комплексные числа
обычные и Unicode-строки
кортежи, списки, множества и словари, содержащие только сериализуемые объекты
Функции, определённые на верхнем уровне модуля.
встроенные функции, определённые на верхнем уровне модуля
классы, определённые на верхнем уровне модуля
экземпляры таких классов, у которых
__dict__или результат вызова__getstate__()является сериализуемым (подробнее см. раздел Протокол pickle).
Попытки сериализовать несериализуемые объекты вызовут исключение PicklingError; когда это происходит, некоторое количество байт может быть уже записано в базовый файл. Попытка сериализовать сильно рекурсивную структуру данных может превысить максимальную глубину рекурсии, в этом случае будет возбуждено исключение RuntimeError. Можно аккуратно увеличить этот предел с помощью sys.setrecursionlimit().
Обратите внимание, что функции (встроенные и определённые пользователем) упаковываются по «полностью квалифицированному» имени, а не по значению. Это означает, что упаковывается только имя функции вместе с именем модуля, в котором функция определена. Ни код функции, ни какие-либо её атрибуты не упаковываются. Поэтому определяющий модуль должен быть импортируемым в среде распаковки, и модуль должен содержать именованный объект, иначе будет возбуждено исключение. 4
Аналогично, классы сериализуются по именованной ссылке, поэтому в среде десериализации действуют те же ограничения. Обратите внимание, что ни код класса, ни его данные не сериализуются, поэтому в следующем примере атрибут класса attr не восстанавливается в среде десериализации:
class Foo:
attr = 'a class attr'
picklestring = pickle.dumps(Foo)
Эти ограничения объясняют, почему сериализуемые функции и классы должны быть определены на верхнем уровне модуля.
Аналогично, при сериализации экземпляров классов код и данные самого класса не сериализуются вместе с ними. Сериализуются только данные экземпляра. Это сделано намеренно, чтобы можно было исправить ошибки в классе или добавить в него методы и по-прежнему загружать объекты, созданные с помощью более старой версии класса. Если планируется использовать долгоживущие объекты, которые переживут множество версий класса, будет разумно добавить в объекты номер версии, чтобы можно было выполнять соответствующие преобразования с помощью метода __setstate__() класса.
11.1.5. Протокол pickle¶The pickle protocol
В этом разделе описывается «протокол pickle», который определяет интерфейс между пиклером/анпиклером и сериализуемыми объектами. Этот протокол предоставляет стандартный способ определения, настройки и управления тем, как объекты сериализуются и десериализуются. Описание в этом разделе не охватывает специальные настройки, которые можно применить для обеспечения большей безопасности среды распаковки от недоверенных потоков данных pickle; см. раздел Создание подклассов Unpickler для получения более подробной информации.
11.1.5.1. Упаковка и распаковка обычных экземпляров классов¶Pickling and unpickling normal class instances
-
object.__getinitargs__()¶ Когда сериализованный экземпляр класса распаковывается, его метод
__init__()обычно не вызывается. Если необходимо, чтобы метод__init__()вызывался при распаковке, класс старого стиля может определить метод__getinitargs__(), который должен возвращать кортеж позиционных аргументов, передаваемых конструктору класса (например,__init__()). Именованные аргументы не поддерживаются. Метод__getinitargs__()вызывается во время упаковки; возвращаемый кортеж включается в pickle для экземпляра.
-
object.__getnewargs__()¶ Типы нового стиля могут предоставлять метод
__getnewargs__(), который используется для протокола 2. Реализация этого метода необходима, если тип устанавливает некоторые внутренние инварианты при создании экземпляра, или если выделение памяти зависит от значений, передаваемых методу__new__()для этого типа (как для кортежей и строк). Экземпляры класса нового стиляCсоздаются с помощьюobj = C.__new__(C, *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__().
11.1.5.2. Упаковка и распаковка типов расширений¶Pickling and unpickling extension types
-
object.__reduce__()¶ Когда
Picklerвстречает объект типа, о котором ничего не знает (например, тип расширения), он ищет в двух местах подсказку, как его упаковать. Один вариант – чтобы объект реализовал метод__reduce__(). Если он предоставлен, при упаковке__reduce__()будет вызван без аргументов и должен вернуть либо строку, либо кортеж.Если возвращается строка, она задаёт имя глобальной переменной, содержимое которой упаковывается обычным образом. Строка, возвращаемая
__reduce__(), должна быть локальным именем объекта относительно его модуля; модуль pickle ищет пространство имён модуля, чтобы определить модуль объекта.Если возвращается кортеж, он должен содержать от двух до пяти элементов. Необязательные элементы можно опустить или передать
Noneв качестве их значения. Содержимое этого кортежа упаковывается как обычно и используется для восстановления объекта при распаковке. Семантика каждого элемента:Вызываемый объект, который будет вызван для создания начальной версии объекта. Следующий элемент кортежа предоставит аргументы для этого вызываемого объекта, а последующие элементы – дополнительную информацию о состоянии, которая затем будет использована для полного восстановления упакованных данных.
В среде распаковки этот объект должен быть классом, вызываемым объектом, зарегистрированным как «безопасный конструктор» (см. ниже), или иметь атрибут
__safe_for_unpickling__с истинным значением. В противном случае в среде распаковки будет возбуждено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. Этот модуль предоставляет способ регистрации «функций сокращения» (reduction functions) и конструкторов для пользовательских типов. Функции сокращения имеют ту же семантику и интерфейс, что и метод __reduce__(), описанный выше, за исключением того, что они вызываются с одним аргументом – упаковываемым объектом.
Зарегистрированный конструктор считается «безопасным конструктором» для целей распаковки, как описано выше.
11.1.5.3. Упаковка и распаковка внешних объектов¶Pickling and unpickling external objects
В интересах сохранения объектов (object persistence) модуль pickle поддерживает понятие ссылки на объект вне потока упакованных данных. Такие объекты обозначаются «постоянным идентификатором» (persistent id), который представляет собой произвольную строку из печатных ASCII-символов. Разрешение таких имён не определяется модулем pickle; он делегирует это разрешение пользовательским функциям упаковщика и распаковщика. 7
Чтобы определить разрешение внешних постоянных идентификаторов, необходимо установить атрибут persistent_id объекта pickler и атрибут persistent_load объекта unpickler.
Чтобы упаковать объекты, имеющие внешний постоянный идентификатор, упаковщик должен иметь пользовательский метод 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 атрибут persistent_load распаковщика также может быть установлен в список Python; в этом случае, когда распаковщик достигает постоянного идентификатора, строка постоянного идентификатора просто добавляется в этот список. Эта функциональность существует для того, чтобы поток данных pickle можно было «пронюхать» на предмет ссылок на объекты без фактического создания всех объектов в pickle. 8 Установка persistent_load в список обычно используется в сочетании с методом noload() на Unpickler.
11.1.6. Создание подклассов Распаковщика¶Subclassing Unpicklers
По умолчанию распаковка импортирует любой класс, который найдет в данных pickle. Вы можете точно контролировать, что будет распаковано и что будет вызвано, настроив свой распаковщик. К сожалению, способ это сделать различается в зависимости от того, используете ли вы pickle или cPickle. 9
В модуле pickle вам нужно создать подкласс от Unpickler, переопределив метод load_global(). load_global() должен читать две строки из потока данных pickle, где первая строка будет именем модуля, содержащего класс, а вторая – именем класса экземпляра. Затем он ищет класс, возможно импортируя модуль и извлекая атрибут, после чего добавляет найденное в стек распаковщика. Позже этот класс будет присвоен атрибуту __class__ пустого класса, что является способом магического создания экземпляра без вызова __init__() его класса. Ваша задача (если вы решите её принять) – сделать так, чтобы load_global() помещал в стек распаковщика известную безопасную версию любого класса, который вы считаете безопасным для распаковки. Создать такой класс – ваша забота. Или вы можете возбудить ошибку, если хотите запретить любую распаковку экземпляров. Если это звучит как хак, вы правы. Обратитесь к исходному коду, чтобы заставить это работать.
С cPickle дела обстоят немного чище, но ненамного. Чтобы контролировать, что будет распаковано, вы можете установить атрибут find_global распаковщика в функцию или None. Если это None, то любые попытки распаковать экземпляры возбудят UnpicklingError. Если это функция, то она должна принимать имя модуля и имя класса и возвращать соответствующий объект класса. Она отвечает за поиск класса и выполнение необходимых импортов, и может возбудить ошибку, чтобы предотвратить распаковку экземпляров этого класса.
Мораль этой истории в том, что нужно быть очень осторожным с источником строк, которые ваше приложение распаковывает.
11.1.7. Пример¶Example
Для простейшего кода используйте функции 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')
# Сериализовать словарь с помощью протокола 0.
pickle.dump(data1, output)
# Сериализовать список с использованием наивысшего доступного протокола.
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:
"""Вывести и пронумеровать строки в текстовом файле."""
def __init__(self, file):
self.file = file
self.fh = open(file)
self.lineno = 0
def readline(self):
self.lineno = self.lineno + 1
line = self.fh.readline()
if not line:
return None
if line.endswith("\n"):
line = line[:-1]
return "%d: %s" % (self.lineno, line)
def __getstate__(self):
odict = self.__dict__.copy() # Скопировать словарь, так как он изменяется
del odict['fh'] # Удалить запись дескриптора файла
return odict
def __setstate__(self, dict):
fh = open(dict['file']) # Переоткрыть файл
count = dict['lineno'] # Читать из файла...
while count: # Пока счётчик строк не восстановится
fh.readline()
count = count - 1
self.__dict__.update(dict) # Обновить атрибуты
self.fh = fh # Сохранить файловый объект
Пример использования может выглядеть так:
>>> 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."""'
11.2. cPickle – более быстрый pickle¶cPickle – A faster pickle
Модуль cPickle поддерживает сериализацию и десериализацию объектов Python,
предоставляя интерфейс и функциональность, почти идентичные модулю
pickle. Есть несколько различий, самое важное – производительность и возможность создания подклассов.
Во-первых, cPickle может быть до 1000 раз быстрее pickle, поскольку
первый реализован на C. Во-вторых, в модуле cPickle вызываемые объекты
Pickler() и Unpickler() являются функциями, а не классами.
Это означает, что их нельзя использовать для создания пользовательских подклассов упаковки и распаковки.
Большинству приложений эта функциональность не нужна, и они могут
воспользоваться значительно улучшенной производительностью модуля cPickle.
Поток данных pickle, создаваемый pickle и cPickle,
идентичен, поэтому можно использовать pickle и cPickle
взаимозаменяемо с существующими pickles. 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 на самом деле является крошечным стековым языком программирования и допускает некоторую свободу в кодировании определённых объектов, возможно, что два модуля создают разные потоки данных для одних и тех же входных объектов. Однако гарантируется, что они всегда смогут читать потоки данных друг друга.