Модуль pickle
Модуль pickle реализует мощный алгоритм сериализации и десериализации объектов Python. «Pickling» — процесс преобразования объекта Python в поток байтов, а «unpickling» — обратная операция, в результате которой поток байтов преобразуется обратно в Python-объект. Так как поток байтов легко можно записать в файл, модуль pickle широко применяется для сохранения и загрузки сложных объектов в Python.
Не загружайте с помощью модуля pickle файлы из ненадёжных источников. Это может привести к необратимым последствиям.
Модуль pickle предоставляет следующие функции для удобства сохранения/загрузки объектов:
pickle.dump(obj, file, protocol=None, *, fix_imports=True) — записывает сериализованный объект в файл. Дополнительный аргумент protocol указывает используемый протокол. По умолчанию равен 3 и именно он рекомендован для использования в Python 3 (несмотря на то, что в Python 3.4 добавили протокол версии 4 с некоторыми оптимизациями). В любом случае, записывать и загружать надо с одним и тем же протоколом.
pickle.dumps(obj, protocol=None, *, fix_imports=True) — возвращает сериализованный объект. Впоследствии вы его можете использовать как угодно.
pickle.load(file, *, fix_imports=True, encoding=»ASCII», errors=»strict») — загружает объект из файла.
pickle.loads(bytes_object, *, fix_imports=True, encoding=»ASCII», errors=»strict») — загружает объект из потока байт.
Модуль pickle также определяет несколько исключений:
- pickle.PickleError
- pickle.PicklingError — случились проблемы с сериализацией объекта.
- pickle.UnpicklingError — случились проблемы с десериализацией объекта.
Этих функций вполне достаточно для сохранения и загрузки встроенных типов данных.
, 'a': [1, 2.0, 3, (4+6j)], 'b': ('character string', b'byte string')>Для вставки кода на Python в комментарий заключайте его в теги
Модуль pickle
Модуль pickle предоставляет функции и классы для сериализации и десериализации объектов. В программировании под сериализацией понимают преобразование каких-либо данных в набор байтов, который потом обычно сохраняют в файл или передают по сети. Десериализация - это восстановление объектов из их байтовых представлений.
Часто сериализация используется для сохранения пользовательских данных между разными сессиями работы приложения, обычно игры. Более простой пример - вы работаете в интерактивном режиме и создали список или словарь, который захотите использовать в следующий раз. Для этого с помощью функции dump() модуля pickle вы сохраняете объект в файл, а с помощь load() восстанавливаете в следующий раз.
>>> a = [1, 10, 0, -3, 9] >>> b = >>> import pickle >>> f = open('data', 'wb') >>> pickle.dump(a, f) >>> pickle.dump(b, f) >>> f.close() >>> del a >>> a Traceback (most recent call last): File "", line 1, in NameError: name 'a' is not defined >>> f = open('data', 'rb') >>> a = pickle.load(f) >>> a [1, 10, 0, -3, 9] >>> c = pickle.load(f) >>> c >>> c == b True >>> c is b FalseВ Python большинство объектов можно сериализовать. Однако не все. Кроме того, следует быть осторожными при сериализации объектов, созданных на основе собственных классов. Сериализуется только объект, но не класс. При десериализации класс импортируется. Поэтому не следует определять классы в том же файле, где будут создаваться и сериализоваться объекты.
Кроме методов dump() и load() есть аналогичные dumps() и loads() , которые работают без файлов. Набор байтов используется в программе.
>>> a = [1, 10, 0, -3, 9] >>> ab = pickle.dumps(a) >>> ab b'\x80\x03]q\x00(K\x01K\nK\x00J\xfd\xff\xff\xffK\te.' >>> b = pickle.loads(ab) >>> b [1, 10, 0, -3, 9] >>> type(ab)
Пользоваться pickle лучше только в рамках python-приложения. При обмене данными между разными языками программирования обычно используют модуль json . Также pickle никак не решает вопрос безопасности данных. Поэтому не следует десериализовать данные из неизвестных источников.
Более ранние версии Python могут не понимать протокол сериализации более поздних версий. Для совместимости в функции можно передавать номер протокола.
>>> pickle.DEFAULT_PROTOCOL 3 >>> pickle.HIGHEST_PROTOCOL 4 >>> a = [1, 2, 3] >>> ab = pickle.dumps(a, 2) >>> b = pickle.loads(ab) >>> b [1, 2, 3]Модуль 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) # # 'b': ('string', 'Unicode string'), # 'c': None> >>> data = pickle.load(pkl_file) >>> pprint.pprint(data2, width=60) # [1, 2, 3, >>> pkl_file.close()
- КРАТКИЙ ОБЗОР МАТЕРИАЛА.
- Какие данные можно сохранять при помощи pickle
- Функции dump() и dumps() модуля pickle
- Функция load() и loads() модуля pickle
- Класс Pickler() модуля pickle
- Класс Unpickler() модуля pickle
- Константы модуля pickle
- Исключения модуля pickle
- Распаковка pickle данных из ненадежных источников
- Сохранение внешних объектов в pickle
- Таблицы диспетчеризации объекта pickle
- Извлечение функций и классов по именам в pickle
Pickles в Python
В памяти компьютера данные хранятся в массивах, списках, хэш-таблицах, … И других структурах данных, оптимизированных для того, чтобы 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 есть несколько минусов.
- Piсkle — это питонский формат сериализации и пиклы не годятся для передачи объектов не то что между разными языками, а иногда даже и между разными версиями интерпретатора Python. Поэтому если в вашей архитектуре есть модули, которые написаны не на Python, то, может быть, pickle для обмена данными — не лучший выбор.
- Pickle не человеко-читаемы. У pickle есть несколько серий протокола, ранние старались делать такими, чтобы их можно было читать глазами, поздние уже нет, но это все равно мало что изменило: если потом захочется поисследовать данные с помощью cat/head/grep и других полезных утилит bash, то вас будет ждать разочарование. Чтобы прочитать pickle с файловой системы, понадобится Python скрипт — и это не очень удобно.
- Pickle не слишком быстрые, по крайней мере, проигрывают модулю json в скорости.
- И последнее: pickle небезопасны. Первое, что написано в документации к модулю — это предупреждение:
Warning The pickle module is not secure. Only unpickle data you trust.
Дело в том, что, применив не очень сложное колдунство, в байтовую строку можно добавить любой код, который pickle выполнит при распаковке. Поэтому избегайте их использования там, где нужна безопасность и распаковывайте только данные из источников, которым доверяете.
В общем, pickle это питонский формат для сериализации данных. Он удобен, когда нужно на скорую руку сохранить состояние объекта. Pickle хорошо использовать в случаях, когда думаете, что файл не проживет долго и когда нет фокуса на вопросах безопасности. Зная все это, там, где можно, я все же выбираю JSON: это быстрее, это читаемее, это безопаснее. Правда, кода нужно писать чуть больше.
