Сообщить об ошибке.

Модуль pickle, упаковка объектов Python

Сериализация и десериализация структуры объекта Python

Модуль pickle реализует двоичные протоколы для сериализации и десериализации структуры объекта Python. "Pickling" - это процесс, посредством которого иерархия объектов Python преобразуется в поток байтов, а "unpickling" - обратная операция, посредством которой поток байтов, из двоичного файла или объекта, подобного байту преобразуется обратно в иерархию объектов.

Чтобы сериализовать иерархию объектов, вы просто вызываете функцию pickle.dumps(). Аналогично, для десериализации потока данных вы вызываете функцию pickle.loads(). Если нужно больше контроля за упаковкой и особенно распаковкой данных, то можно создать объект pickle.Pickler() или pickle.Unpickler() соответственно.

Для справки: функции, названия которых оканчиваются на символ 's' (damps, loads) - работают со строками.

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

  • Модуль pickle не защищен. Распаковывайте данные только те, которым вы доверяете. Можно сериализовать данные, которые будут выполнять произвольный код во время распаковки. Никогда не извлекайте данные, которые могли прийти из ненадежного источника или могли быть подделаны.
  • Подписывайте данные с помощью модуля hmac для того, чтобы быть уверенным, что они не были подделаны.
  • Если часто приходится обрабатывать ненадежные данные, то используйте более безопасный формат сериализации, такие как json.

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

Сравнение с JSON.

Существуют фундаментальные различия между протоколами Pickle и JSON:

  • JSON - это текстовый формат сериализации, а pickle - это двоичный формат сериализации;
  • JSON читается человеком, а pickle - нет;
  • JSON широко используется за пределами экосистемы Python, в то время как Pickle зависит от Python;

По умолчанию JSON может представлять только подмножество встроенных типов Python, а не пользовательские классы. Pickle может представлять чрезвычайно большое количество типов Python. Сложные случаи могут быть решены путем реализации определенных объектных API.

В отличие от pickle, десериализация ненадежного JSON сама по себе не создает уязвимости при выполнении произвольного кода.

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

Формат данных, используемый модулем pickle зависит от Python. Это имеет то преимущество, что нет никаких ограничений, налагаемых внешними стандартами, такими как JSON или XDR, которые не могут представлять совместное использование указателей. Это означает, что программы, не являющиеся Python, могут не иметь возможности реконструировать выбранные объекты Python.

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

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

  • Протокол версии 0 является исходным "читаемым человеком" протоколом и обратно совместим с более ранними версиями Python.
  • Протокол версии 1 - это старый двоичный формат, который также совместим с более ранними версиями Python.
  • Протокол версии 2 был представлен в Python 2.3. Это обеспечивает намного более эффективную сборку классов нового стиля.
  • Протокол версии 3 был добавлен в Python 3.0. Он имеет явную поддержку байтовых объектов и не может быть распакован Python 2.x. Это был протокол по умолчанию в Python 3.0–3.7.
  • Протокол версии 4 был добавлен в Python 3.4. Добавлена ​​поддержка очень больших объектов, выборка большего количества типов объектов и некоторые оптимизации форматов данных. Это протокол по умолчанию, начиная с Python 3.8.
  • Протокол версии 5 был добавлен в Python 3.8. Добавлена ​​поддержка внеполосных данных и ускорение внутриполосных данных.

Примечание.
Сериализация - более примитивное понятие, чем постоянство. Хотя pickle читает и записывает файловые объекты, он не решает проблему именования постоянных объектов, а также, даже более сложную проблему одновременного доступа к постоянным объектам. Модуль pickle может преобразовать сложный объект в поток байтов и может преобразовать поток байтов в объект с такой же внутренней структурой. Возможно, наиболее очевидная вещь, которую нужно сделать с этими потоками байтов - это записать их в файл, но также возможно отправить их по сети или сохранить их в базе данных. Модуль shelve предоставляет простой интерфейс для выбора и удаления объектов в файлах базы данных в стиле DBM.

Примеры использования:

Простейший код использования dump() и load() функцию.

>>> import pickle
# используемый протокол по умолчанию
>>> pickle.DEFAULT_PROTOCOL
# 3

# создадим данные для записи
>>> 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.dump(data1, output)
# список запишем по протоколу 4
>>> pickle.dump(selfref_list, output, 4)
# закрываем файл
>>> output.close()

В следующем примере считываются упакованные данные.

>>> import pprint, pickle
>>> pkl_file = open('data.pkl', 'rb')
>>> data1 = pickle.load(pkl_file)
>>> pprint.pprint(data1, width=60)
# {'a': [1, 2.0, 3, (4+6j)],
# 'b': ('string', 'Unicode string'),
# 'c': None}
>>> data = pickle.load(pkl_file)
>>> pprint.pprint(data2, width=60)
# [1, 2, 3, <Recursion on list with id=139710613355592>]
>>> pkl_file.close()