Pickles в Python

Автор: CoolPython

В памяти компьютера данные хранятся в массивах, списках, хэш-таблицах, … И других структурах данных, оптимизированных для того, чтобы CPU мог эффективно обращаться к ним и манипулировать ими. Но если требуется записать данные в файлы или передать их по сети, то нужно представить их в виде последовательности байтов. Преобразование из первого представления данных во второе называется сериализацией, обратное — десериализацией.

Во многих языках программирования есть встроенные инструменты для сериализации. Например, в Java есть java.io.Serializeable, в Ruby — Marshal, а в Python есть pickle. Смотрите, как просто этим инструментом сериализовать почти любой питонский объект:

import pickle

original = [1, 2, 3]

with open('myfile.pickle', 'wb') as outfile:
    pickle.dump(original, outfile)
with open('myfile.pickle', 'rb') as infile:
    identical = pickle.load(infile)

print(original == identical)  # True

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

Как это работает? Идея в том, что pickle содержит информацию о том, как реконструировать объект. Если мы попробуем запринтить строку, которую формирует pickle после сериализации списка выше, то увидим две вещи: ссылку на объявление класса, какое-то количество нечитаемых символов, и значения полей. Приблизительно это читается интерпретатором как “возьми вот этот класс и положи в него значения 1, 2, 3”.

Обратите внимание, что мы сохраняем состояние объекта, а не объявление. То есть, мы знаем содержимое полей этого класса, но остаемся в неведении относительно его структуры. Это важно для пользовательских типов, потому что в программе, в которой мы собираемся распаковывать pickle, этот класс должен быть объявлен. Иначе мы не сможем его распаковать — непонятно, какой именно объект инициализировать этими данными.

За простоту использования pickle полюбили в data science. Оказалось удобно сохранять промежуточные результаты вычислений в полях класса, чтобы к расчетам можно было вернуться позже, без необходимости обучать модель заново.

Но у pickle есть несколько минусов.

  1. Piсkle — это питонский формат сериализации и пиклы не годятся для передачи объектов не то что между разными языками, а иногда даже и между разными версиями интерпретатора Python. Поэтому если в вашей архитектуре есть модули, которые написаны не на Python, то, может быть, pickle для обмена данными — не лучший выбор.
  2. Pickle не человеко-читаемы. У pickle есть несколько серий протокола, ранние старались делать такими, чтобы их можно было читать глазами, поздние уже нет, но это все равно мало что изменило: если потом захочется поисследовать данные с помощью cat/head/grep и других полезных утилит bash, то вас будет ждать разочарование. Чтобы прочитать pickle с файловой системы, понадобится Python скрипт — и это не очень удобно.
  3. Pickle не слишком быстрые, по крайней мере, проигрывают модулю json в скорости.
  4. И последнее: pickle небезопасны. Первое, что написано в документации к модулю — это предупреждение:
Warning The pickle module is not secure. Only unpickle data you trust.

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

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