pickle — Python object serialization

Вихідний код: Lib/pickle.py


Модуль pickle реалізує двійкові протоколи для серіалізації та десеріалізації структури об’єктів Python. «Pickling» - це процес, за допомогою якого ієрархія об’єктів Python перетворюється на потік байтів, а «unpickling» - це зворотна операція, за допомогою якої потік байтів (з binary file або bytes-like object) перетворюється назад в ієрархію об’єктів. Травлення (і розбирання) також відоме як «серіалізація», «маршалінг», [1] або «зведення»; однак, щоб уникнути плутанини, тут використовуються терміни «маринування» та «розмаринування».

Попередження

Модуль pickle не захищений. Вилучайте лише дані, яким довіряєте.

Можна створити шкідливі дані pickle, які виконуватимуть довільний код під час unpickling. Ніколи не видаляйте дані, які могли надійти з ненадійного джерела або які могли бути підроблені.

Розгляньте можливість підписати дані за допомогою hmac, якщо вам потрібно переконатися, що вони не були змінені.

Більш безпечні формати серіалізації, такі як json, можуть бути більш доречними, якщо ви обробляєте ненадійні дані. Перегляньте Порівняння з json.

Зв’язок з іншими модулями Python

Порівняння з маршалом

Python має примітивніший модуль серіалізації під назвою marshal, але загалом pickle має завжди бути кращим способом серіалізації об’єктів Python. marshal існує в основному для підтримки файлів .pyc Python.

Модуль pickle відрізняється від marshal кількома суттєвими ознаками:

  • Модуль pickle відстежує об’єкти, які він уже серіалізував, так що пізніші посилання на той самий об’єкт не будуть серіалізовані знову. marshal цього не робить.

    Це стосується як рекурсивних об’єктів, так і спільного використання об’єктів. Рекурсивні об’єкти - це об’єкти, які містять посилання на себе. Вони не обробляються маршалом, і фактично спроба маршалу рекурсивних об’єктів призведе до збою вашого інтерпретатора Python. Спільне використання об’єктів відбувається, коли існує кілька посилань на той самий об’єкт у різних місцях ієрархії об’єктів, що серіалізується. pickle зберігає такі об’єкти лише один раз і гарантує, що всі інші посилання вказують на головну копію. Спільні об’єкти залишаються спільними, що може бути дуже важливим для змінних об’єктів.

  • marshal не можна використовувати для серіалізації визначених користувачем класів та їх екземплярів. pickle може прозоро зберігати та відновлювати екземпляри класу, однак визначення класу має бути імпортованим і знаходитись у тому самому модулі, що й під час зберігання об’єкта.

  • Формат серіалізації marshal не гарантовано переноситься між версіями Python. Оскільки основним завданням у житті є підтримка файлів .pyc, розробники Python залишають за собою право змінювати формат серіалізації несумісними способами, якщо виникне така потреба. Формат серіалізації pickle гарантовано буде зворотно сумісним з усіма випусками Python за умови, що вибрано сумісний протокол pickle, а код піклування та депіклування враховує відмінності типів Python 2 і Python 3, якщо ваші дані перетинають цю унікальну межу мови зміни порушення. .

Порівняння з json

There are fundamental differences between the pickle protocols and JSON (JavaScript Object Notation):

  • JSON — це формат текстової серіалізації (він виводить текст Юнікод, хоча здебільшого він потім кодується у utf-8), тоді як pickle — це двійковий формат серіалізації;

  • JSON читається людиною, а pickle – ні;

  • JSON є сумісним і широко використовується за межами екосистеми Python, тоді як pickle є специфічним для Python;

  • За замовчуванням JSON може представляти лише підмножину вбудованих типів Python, а не спеціальні класи; pickle може представляти надзвичайно велику кількість типів Python (багато з них автоматично, завдяки розумному використанню можливостей інтроспекції Python; складні випадки можна вирішити шляхом впровадження спеціальних API об’єктів);

  • На відміну від pickle, десеріалізація ненадійного JSON сама по собі не створює вразливості виконання довільного коду.

Дивись також

Модуль json: стандартний бібліотечний модуль, який дозволяє серіалізацію та десеріалізацію JSON.

Формат потоку даних

The data format used by pickle is Python-specific. This has the advantage that there are no restrictions imposed by external standards such as JSON or XDR (which can’t represent pointer sharing); however it means that non-Python programs may not be able to reconstruct pickled Python objects.

За замовчуванням формат даних pickle використовує відносно компактне двійкове представлення. Якщо вам потрібні оптимальні характеристики розміру, ви можете ефективно стиснути мариновані дані.

Модуль pickletools містить інструменти для аналізу потоків даних, згенерованих pickle. Вихідний код pickletools містить численні коментарі щодо кодів операцій, які використовуються протоколами pickle.

На даний момент існує 6 різних протоколів, які можна використовувати для маринування. Чим вищий протокол використовується, тим новіша версія Python потрібна для читання створеного пікле.

  • Протокол версії 0 — це оригінальний «людиночитаний» протокол, який зворотно сумісний із попередніми версіями Python.

  • Протокол версії 1 — це старий двійковий формат, який також сумісний із попередніми версіями Python.

  • Протокол версії 2 був представлений у Python 2.3. Він забезпечує набагато ефективніше маринування класів нового стилю. Зверніться до PEP 307, щоб отримати інформацію про покращення, внесені протоколом 2.

  • Протокол версії 3 додано в Python 3.0. Він має явну підтримку об’єктів bytes і не може бути скасований Python 2.x. Це був протокол за замовчуванням у Python 3.0-3.7.

  • Протокол версії 4 додано в Python 3.4. Він додає підтримку для дуже великих об’єктів, маринування більшої кількості типів об’єктів і деяку оптимізацію формату даних. Це стандартний протокол, починаючи з Python 3.8. Зверніться до PEP 3154 для отримання інформації про покращення, внесені протоколом 4.

  • Протокол версії 5 додано в Python 3.8. Він додає підтримку позасмугових даних і прискорення для внутрішньосмугових даних. Зверніться до PEP 574 для отримання інформації про покращення, внесені протоколом 5.

Примітка

Серіалізація є більш примітивним поняттям, ніж стійкість; хоча pickle читає та записує файлові об’єкти, він не вирішує проблему іменування постійних об’єктів, ані (навіть більш складну) проблему одночасного доступу до постійних об’єктів. Модуль pickle може перетворити складний об’єкт у потік байтів, і він може перетворити потік байтів в об’єкт із такою ж внутрішньою структурою. Мабуть, найочевидніша річ, яку можна зробити з цими потоками байтів, — це записати їх у файл, але також можливо надіслати їх через мережу або зберегти в базі даних. Модуль shelve забезпечує простий інтерфейс для вибору та вилучення об’єктів у файлах бази даних у стилі DBM.

Інтерфейс модуля

Щоб серіалізувати ієрархію об’єктів, ви просто викликаєте функцію dumps(). Так само, щоб десеріалізувати потік даних, ви викликаєте функцію loads(). Однак, якщо ви хочете більше контролювати серіалізацію та десеріалізацію, ви можете створити об’єкт Pickler або Unpickler відповідно.

Модуль pickle надає такі константи:

pickle.HIGHEST_PROTOCOL

Ціле число, найвища доступна версія протоколу. Це значення можна передати як значення protocol до функцій dump() і dumps(), а також до конструктора Pickler.

pickle.DEFAULT_PROTOCOL

Ціле число, стандартна версія протоколу, що використовується для травлення. Може бути менше ніж HIGHEST_PROTOCOL. Наразі стандартним протоколом є 4, вперше представлений у Python 3.4 і несумісний із попередніми версіями.

Змінено в версії 3.0: Стандартний протокол – 3.

Змінено в версії 3.8: Стандартний протокол – 4.

Модуль pickle надає такі функції, щоб зробити процес маринування зручнішим:

pickle.dump(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None)

Запишіть виділене представлення об’єкта obj у відкритий file object file. Це еквівалентно Pickler(file, protocol).dump(obj).

Аргументи file, protocol, fix_imports і buffer_callback мають те саме значення, що й у конструкторі Pickler.

Змінено в версії 3.8: Додано аргумент buffer_callback.

pickle.dumps(obj, protocol=None, *, fix_imports=True, buffer_callback=None)

Повернути відібране представлення об’єкта obj як об’єкт bytes замість запису його у файл.

Аргументи protocol, fix_imports і buffer_callback мають те саме значення, що й у конструкторі Pickler.

Змінено в версії 3.8: Додано аргумент buffer_callback.

pickle.load(file, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None)

Зчитувати виділене представлення об’єкта з відкритого file object file і повертати відновлену ієрархію об’єктів, указану в ньому. Це еквівалентно Unpickler(file).load().

Версія протоколу pickle визначається автоматично, тому аргумент протоколу не потрібен. Байти, що перебувають після виділеного представлення об’єкта, ігноруються.

Аргументи file, fix_imports, encoding, errors, strict і buffers мають те саме значення, що й у конструкторі Unpickler.

Змінено в версії 3.8: Додано аргумент buffers.

pickle.loads(data, /, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None)

Повертає відновлену ієрархію об’єктів маринованого представлення даних об’єкта. data має бути bytes-like object.

Версія протоколу pickle визначається автоматично, тому аргумент протоколу не потрібен. Байти, що перебувають після виділеного представлення об’єкта, ігноруються.

Аргументи fix_imports, encoding, errors, strict і buffers мають те саме значення, що й у конструкторі Unpickler.

Змінено в версії 3.8: Додано аргумент buffers.

Модуль pickle визначає три винятки:

exception pickle.PickleError

Common base class for the other pickling exceptions. It inherits from Exception.

exception pickle.PicklingError

Error raised when an unpicklable object is encountered by Pickler. It inherits from PickleError.

Зверніться до Що можна маринувати і не квашети?, щоб дізнатися, які види об’єктів можна маринувати.

exception pickle.UnpicklingError

Error raised when there is a problem unpickling an object, such as a data corruption or a security violation. It inherits from PickleError.

Зауважте, що інші винятки також можуть виникати під час видалення, включаючи (але не обов’язково обмежуючись ними) AttributeError, EOFError, ImportError та IndexError.

Модуль pickle експортує три класи, Pickler, Unpickler і PickleBuffer:

class pickle.Pickler(file, protocol=None, *, fix_imports=True, buffer_callback=None)

Це бере двійковий файл для запису потоку даних pickle.

Необов’язковий аргумент protocol, ціле число, повідомляє піклеру використовувати заданий протокол; підтримувані протоколи від 0 до HIGHEST_PROTOCOL. Якщо не вказано, типовим є DEFAULT_PROTOCOL. Якщо вказано від’ємне число, вибирається HIGHEST_PROTOCOL.

Аргумент file повинен мати метод write(), який приймає однобайтовий аргумент. Таким чином, це може бути файл на диску, відкритий для двійкового запису, екземпляр io.BytesIO або будь-який інший спеціальний об’єкт, який відповідає цьому інтерфейсу.

Якщо fix_imports має значення true, а protocol — менше 3, pickle спробує зіставити нові імена Python 3 зі старими назвами модулів, які використовуються в Python 2, щоб потік даних pickle можна було читати за допомогою Python 2.

If buffer_callback is None (the default), buffer views are serialized into file as part of the pickle stream.

If buffer_callback is not None, then it can be called any number of times with a buffer view. If the callback returns a false value (such as None), the given buffer is out-of-band; otherwise the buffer is serialized in-band, i.e. inside the pickle stream.

It is an error if buffer_callback is not None and protocol is None or smaller than 5.

Змінено в версії 3.8: Додано аргумент buffer_callback.

dump(obj)

Запишіть виділене представлення obj до відкритого файлового об’єкта, наданого в конструкторі.

persistent_id(obj)

За замовчуванням нічого не робити. Це існує, тому підклас може замінити його.

Якщо persistent_id() повертає None, obj маринується як зазвичай. Будь-яке інше значення змушує Pickler видати повернуте значення як постійний ідентифікатор для obj. Значення цього постійного ідентифікатора має бути визначено Unpickler.persistent_load(). Зауважте, що значення, яке повертає persistent_id(), не може мати постійний ідентифікатор.

Перегляньте Постійність зовнішніх об’єктів для деталей та прикладів використання.

dispatch_table

A pickler object’s dispatch table is a registry of reduction functions of the kind which can be declared using copyreg.pickle(). It is a mapping whose keys are classes and whose values are reduction functions. A reduction function takes a single argument of the associated class and should conform to the same interface as a __reduce__() method.

За замовчуванням об’єкт pickler не матиме атрибута dispatch_table, натомість він використовуватиме глобальну таблицю відправлення, якою керує модуль copyreg. Однак, щоб налаштувати травлення для певного об’єкта піклеру, можна встановити атрибут dispatch_table на об’єкт, подібний до dict. Крім того, якщо підклас Pickler має атрибут dispatch_table, тоді він використовуватиметься як типова таблиця відправлення для екземплярів цього класу.

Перегляньте Таблиці відправлення приклади використання.

Нове в версії 3.3.

reducer_override(obj)

Special reducer that can be defined in Pickler subclasses. This method has priority over any reducer in the dispatch_table. It should conform to the same interface as a __reduce__() method, and can optionally return NotImplemented to fallback on dispatch_table-registered reducers to pickle obj.

Детальний приклад див. Спеціальне скорочення для типів, функцій та інших об’єктів.

Нове в версії 3.8.

fast

Застаріле. Увімкнути швидкий режим, якщо встановлено справжнє значення. Швидкий режим вимикає використання memo, таким чином прискорюючи процес маринування, не генеруючи зайвих кодів операцій PUT. Його не слід використовувати з об’єктами, що посилаються на себе, інакше призведе до безкінечної рекурсії Pickler.

Використовуйте pickletools.optimize(), якщо вам потрібні більш компактні соління.

class pickle.Unpickler(file, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None)

Це бере двійковий файл для читання потоку даних pickle.

Версія протоколу pickle визначається автоматично, тому аргумент протоколу не потрібен.

Аргумент file повинен мати три методи: метод read(), який приймає цілочисельний аргумент, метод readinto(), який приймає аргумент буфера, і метод readline(), який не потребує аргументів, як у Інтерфейс io.BufferedIOBase. Таким чином, file може бути файлом на диску, відкритим для двійкового читання, об’єктом io.BytesIO або будь-яким іншим спеціальним об’єктом, який відповідає цьому інтерфейсу.

Необов’язкові аргументи fix_imports, encoding і errors використовуються для керування підтримкою сумісності для потоку pickle, створеного Python 2. Якщо fix_imports має значення true, pickle спробує зіставити старі імена Python 2 з новими іменами, які використовуються у Python 3. кодування та помилки повідомляють pickle, як декодувати 8-бітні екземпляри рядків, вибрані Python 2; за замовчуванням вони мають значення «ASCII» і «strict» відповідно. Кодуванням може бути «байт» для читання цих 8-бітних екземплярів рядка як об’єктів bytes. Використання encoding='latin1' потрібне для видалення масивів NumPy і екземплярів datetime, date і time, вибраних Python 2.

If buffers is None (the default), then all data necessary for deserialization must be contained in the pickle stream. This means that the buffer_callback argument was None when a Pickler was instantiated (or when dump() or dumps() was called).

If buffers is not None, it should be an iterable of buffer-enabled objects that is consumed each time the pickle stream references an out-of-band buffer view. Such buffers have been given in order to the buffer_callback of a Pickler object.

Змінено в версії 3.8: Додано аргумент buffers.

load()

Зчитувати виділене представлення об’єкта з відкритого файлового об’єкта, наданого в конструкторі, і повертати відновлену ієрархію об’єктів, указану в ньому. Байти, які перебувають після виділеного представлення об’єкта, ігноруються.

persistent_load(pid)

Викликати UnpicklingError за замовчуванням.

Якщо визначено, persistent_load() має повертати об’єкт, указаний постійним ідентифікатором pid. Якщо виявлено недійсний постійний ідентифікатор, має бути викликано UnpicklingError.

Перегляньте Постійність зовнішніх об’єктів для деталей та прикладів використання.

find_class(module, name)

Імпортуйте module, якщо необхідно, і поверніть з нього об’єкт із назвою name, де аргументи module і name є об’єктами str. Зверніть увагу, на відміну від назви, find_class() також використовується для пошуку функцій.

Підкласи можуть замінити це, щоб отримати контроль над тим, який тип об’єктів і як їх можна завантажувати, потенційно зменшуючи ризики безпеки. Зверніться до Обмеження Globals для деталей.

Викликає подію аудиту pickle.find_class з аргументами module, name.

class pickle.PickleBuffer(buffer)

Обгортка для буфера, що представляє дані, які можна вибрати. buffer має бути об’єктом, що надає буфер, наприклад bytes-like object або N-вимірним масивом.

PickleBuffer сам є постачальником буфера, тому його можна передати іншим API, які очікують об’єкта, що надає буфер, наприклад memoryview.

Об’єкти PickleBuffer можна серіалізувати лише за допомогою протоколу pickle версії 5 або вище. Вони підходять для позасмугової серіалізації.

Нове в версії 3.8.

raw()

Повертає memoryview області пам’яті, що лежить в основі цього буфера. Повернений об’єкт є одновимірним C-суміжним представленням пам’яті у форматі B (байти без знаку). BufferError виникає, якщо буфер не суміжний ні на C, ні на Fortran.

release()

Вивільніть базовий буфер, відкритий об’єктом PickleBuffer.

Що можна маринувати і не квашети?

Маринувати можна такі види:

  • built-in constants (None, True, False, Ellipsis, and NotImplemented);

  • цілі числа, числа з плаваючою комою, комплексні числа;

  • рядки, байти, байтові масиви;

  • кортежі, списки, набори та словники, що містять лише об’єкти, які можна вибирати;

  • функції (вбудовані та визначені користувачем), доступні з верхнього рівня модуля (за допомогою def, а не lambda);

  • класи, доступні з верхнього рівня модуля;

  • instances of such classes whose the result of calling __getstate__() is picklable (see section Примірники класу маринування for details).

Спроби маринувати об’єкти, які неможливо вибрати, викличуть виняток PicklingError; коли це трапляється, невизначену кількість байтів, можливо, уже було записано до основного файлу. Спроба відібрати високорекурсивну структуру даних може перевищити максимальну глибину рекурсії, у цьому випадку виникне RecursionError. Ви можете обережно збільшити це обмеження за допомогою sys.setrecursionlimit().

Зауважте, що функції (вбудовані та визначені користувачем) вибираються за повним qualified name, а не за значенням. [2] Це означає, що виділяється лише назва функції, а також назва модуля та класів, що містять. Ні код функції, ні будь-які її атрибути функції не мариновані. Таким чином, визначальний модуль має бути імпортованим у середовищі unpickling, і модуль має містити названий об’єкт, інакше буде створено виняток. [3]

Подібним чином, класи вибираються за повним іменем, тому застосовуються ті самі обмеження в середовищі розбирання. Зауважте, що жоден із коду чи даних класу не вибирається, тому в наступному прикладі атрибут класу attr не відновлюється в середовищі розбирання:

class Foo:
    attr = 'A class attribute'

picklestring = pickle.dumps(Foo)

Через ці обмеження функції та класи, які можна вибрати, повинні бути визначені на верхньому рівні модуля.

Similarly, when class instances are pickled, their class’s code and data are not pickled along with them. Only the instance data are pickled. This is done on purpose, so you can fix bugs in a class or add methods to the class and still load objects that were created with an earlier version of the class. If you plan to have long-lived objects that will see many versions of a class, it may be worthwhile to put a version number in the objects so that suitable conversions can be made by the class’s __setstate__() method.

Примірники класу маринування

У цьому розділі ми описуємо загальні механізми, доступні вам для визначення, налаштування та контролю того, як екземпляри класу вибираються та не вибираються.

In most cases, no additional code is needed to make instances picklable. By default, pickle will retrieve the class and the attributes of an instance via introspection. When a class instance is unpickled, its __init__() method is usually not invoked. The default behaviour first creates an uninitialized instance and then restores the saved attributes. The following code shows an implementation of this behaviour:

def save(obj):
    return (obj.__class__, obj.__dict__)

def restore(cls, attributes):
    obj = cls.__new__(cls)
    obj.__dict__.update(attributes)
    return obj

Класи можуть змінювати типову поведінку, надаючи один або декілька спеціальних методів:

object.__getnewargs_ex__()

У протоколах 2 і новіших класи, які реалізують метод __getnewargs_ex__(), можуть диктувати значення, що передаються в метод __new__() після видалення. Метод має повертати пару «(args, kwargs)», де args — це кортеж позиційних аргументів, а kwargs — словник іменованих аргументів для побудови об’єкта. Вони будуть передані в метод __new__() після видалення.

Ви повинні застосувати цей метод, якщо метод __new__() вашого класу вимагає аргументів лише з ключовими словами. В іншому випадку для сумісності рекомендується реалізувати __getnewargs__().

Змінено в версії 3.6: __getnewargs_ex__() тепер використовується в протоколах 2 і 3.

object.__getnewargs__()

Цей метод виконує таку саму мету, як __getnewargs_ex__(), але підтримує лише позиційні аргументи. Він має повертати кортеж аргументів args, який буде передано методу __new__() після видалення.

__getnewargs__() не буде викликано, якщо __getnewargs_ex__() визначено.

Змінено в версії 3.6: До Python 3.6 __getnewargs__() викликався замість __getnewargs_ex__() у протоколах 2 і 3.

object.__getstate__()

Classes can further influence how their instances are pickled by overriding the method __getstate__(). It is called and the returned object is pickled as the contents for the instance, instead of a default state. There are several cases:

  • For a class that has no instance __dict__ and no __slots__, the default state is None.

  • For a class that has an instance __dict__ and no __slots__, the default state is self.__dict__.

  • For a class that has an instance __dict__ and __slots__, the default state is a tuple consisting of two dictionaries: self.__dict__, and a dictionary mapping slot names to slot values. Only slots that have a value are included in the latter.

  • For a class that has __slots__ and no instance __dict__, the default state is a tuple whose first item is None and whose second item is a dictionary mapping slot names to slot values described in the previous bullet.

Змінено в версії 3.11: Added the default implementation of the __getstate__() method in the object class.

object.__setstate__(state)

Якщо під час розбирання клас визначає __setstate__(), він викликається зі станом unpickled. У цьому випадку немає вимоги, щоб об’єкт стану був словником. В іншому випадку маринований стан має бути словником, а його елементи призначаються словнику нового екземпляра.

Примітка

If __reduce__() returns a state with value None at pickling, the __setstate__() method will not be called upon unpickling.

Refer to the section Обробка об’єктів із збереженням стану for more information about how to use the methods __getstate__() and __setstate__().

Примітка

At unpickling time, some methods like __getattr__(), __getattribute__(), or __setattr__() may be called upon the instance. In case those methods rely on some internal invariant being true, the type should implement __new__() to establish such an invariant, as __init__() is not called when unpickling an instance.

As we shall see, pickle does not use directly the methods described above. In fact, these methods are part of the copy protocol which implements the __reduce__() special method. The copy protocol provides a unified interface for retrieving the data necessary for pickling and copying objects. [4]

Although powerful, implementing __reduce__() directly in your classes is error prone. For this reason, class designers should use the high-level interface (i.e., __getnewargs_ex__(), __getstate__() and __setstate__()) whenever possible. We will show, however, cases where using __reduce__() is the only option or leads to more efficient pickling or both.

object.__reduce__()

На даний момент інтерфейс визначається наступним чином. Метод __reduce__() не приймає аргументів і повертає або рядок, або, бажано, кортеж (повернений об’єкт часто називають «зменшеним значенням»).

Якщо повертається рядок, цей рядок слід інтерпретувати як назву глобальної змінної. Це має бути локальне ім’я об’єкта відносно його модуля; модуль pickle шукає простір імен модуля, щоб визначити модуль об’єкта. Така поведінка, як правило, корисна для одиночних користувачів.

Коли повертається кортеж, він має містити від двох до шести елементів. Необов’язкові елементи можна або пропустити, або в якості їхнього значення можна вказати None. Семантика кожного елемента в порядку:

  • Викликаний об’єкт, який буде викликано для створення початкової версії об’єкта.

  • Кортеж аргументів для викликаного об’єкта. Порожній кортеж повинен бути наданий, якщо викликаний не приймає жодних аргументів.

  • Необов’язково, стан об’єкта, який буде передано методу __setstate__() об’єкта, як описано раніше. Якщо об’єкт не має такого методу, значення має бути словником, і воно буде додано до атрибута об’єкта __dict__.

  • Optionally, an iterator (and not a sequence) yielding successive items. These items will be appended to the object either using obj.append(item) or, in batch, using obj.extend(list_of_items). This is primarily used for list subclasses, but may be used by other classes as long as they have append and extend methods with the appropriate signature. (Whether append() or extend() is used depends on which pickle protocol version is used as well as the number of items to append, so both must be supported.)

  • Необов’язково, ітератор (не послідовність), що дає послідовні пари ключ-значення. Ці елементи будуть збережені в об’єкті за допомогою obj[key] = value. Це в основному використовується для підкласів словників, але може використовуватися іншими класами, якщо вони реалізують __setitem__().

  • Необов’язково, викликається з підписом (obj, state). Цей виклик дозволяє користувачеві програмно керувати поведінкою оновлення стану конкретного об’єкта замість використання статичного методу obj __setstate__(). Якщо не None, цей виклик матиме пріоритет над obj __setstate__().

    Нове в версії 3.8: Додано необов’язковий шостий елемент кортежу, (obj, state).

object.__reduce_ex__(protocol)

В якості альтернативи можна визначити метод __reduce_ex__(). Єдина відмінність полягає в тому, що цей метод має приймати один цілий аргумент, версію протоколу. Якщо визначено, pickle віддасть перевагу цьому методу __reduce__(). Крім того, __reduce__() автоматично стає синонімом розширеної версії. Основним використанням цього методу є надання зворотно сумісних значень зменшення для старіших випусків Python.

Постійність зовнішніх об’єктів

На користь збереження об’єкта модуль pickle підтримує поняття посилання на об’єкт за межами марінованого потоку даних. На такі об’єкти посилається постійний ідентифікатор, який має бути або рядком буквено-цифрових символів (для протоколу 0) [5], або просто довільним об’єктом (для будь-якого новішого протоколу).

Роздільна здатність таких постійних ідентифікаторів не визначається модулем pickle; він делегує це вирішення визначеним користувачем методам pickler і unpickler, persistent_id() і persistent_load() відповідно.

Щоб вибирати об’єкти, які мають зовнішній постійний ідентифікатор, засіб вибору повинен мати спеціальний метод persistent_id(), який приймає об’єкт як аргумент і повертає «Немає» або постійний ідентифікатор для цього об’єкта. Коли повертається None, піклер просто маринує об’єкт як зазвичай. Коли повертається рядок постійного ідентифікатора, засіб вибору виділяє цей об’єкт разом із маркером, щоб засіб вилучення розпізнавало його як постійний ідентифікатор.

Щоб розібрати зовнішні об’єкти, розбірник повинен мати спеціальний метод persistent_load(), який приймає об’єкт постійного ідентифікатора та повертає об’єкт, на який посилається.

Ось вичерпний приклад, який демонструє, як постійний ідентифікатор можна використовувати для маринування зовнішніх об’єктів за посиланням.

# Simple example presenting how persistent ID can be used to pickle
# external objects by reference.

import pickle
import sqlite3
from collections import namedtuple

# Simple class representing a record in our database.
MemoRecord = namedtuple("MemoRecord", "key, task")

class DBPickler(pickle.Pickler):

    def persistent_id(self, obj):
        # Instead of pickling MemoRecord as a regular class instance, we emit a
        # persistent ID.
        if isinstance(obj, MemoRecord):
            # Here, our persistent ID is simply a tuple, containing a tag and a
            # key, which refers to a specific record in the database.
            return ("MemoRecord", obj.key)
        else:
            # If obj does not have a persistent ID, return None. This means obj
            # needs to be pickled as usual.
            return None


class DBUnpickler(pickle.Unpickler):

    def __init__(self, file, connection):
        super().__init__(file)
        self.connection = connection

    def persistent_load(self, pid):
        # This method is invoked whenever a persistent ID is encountered.
        # Here, pid is the tuple returned by DBPickler.
        cursor = self.connection.cursor()
        type_tag, key_id = pid
        if type_tag == "MemoRecord":
            # Fetch the referenced record from the database and return it.
            cursor.execute("SELECT * FROM memos WHERE key=?", (str(key_id),))
            key, task = cursor.fetchone()
            return MemoRecord(key, task)
        else:
            # Always raises an error if you cannot return the correct object.
            # Otherwise, the unpickler will think None is the object referenced
            # by the persistent ID.
            raise pickle.UnpicklingError("unsupported persistent object")


def main():
    import io
    import pprint

    # Initialize and populate our database.
    conn = sqlite3.connect(":memory:")
    cursor = conn.cursor()
    cursor.execute("CREATE TABLE memos(key INTEGER PRIMARY KEY, task TEXT)")
    tasks = (
        'give food to fish',
        'prepare group meeting',
        'fight with a zebra',
        )
    for task in tasks:
        cursor.execute("INSERT INTO memos VALUES(NULL, ?)", (task,))

    # Fetch the records to be pickled.
    cursor.execute("SELECT * FROM memos")
    memos = [MemoRecord(key, task) for key, task in cursor]
    # Save the records using our custom DBPickler.
    file = io.BytesIO()
    DBPickler(file).dump(memos)

    print("Pickled records:")
    pprint.pprint(memos)

    # Update a record, just for good measure.
    cursor.execute("UPDATE memos SET task='learn italian' WHERE key=1")

    # Load the records from the pickle data stream.
    file.seek(0)
    memos = DBUnpickler(file, conn).load()

    print("Unpickled records:")
    pprint.pprint(memos)


if __name__ == '__main__':
    main()

Таблиці відправлення

Якщо ви хочете налаштувати маринування деяких класів, не порушуючи будь-який інший код, який залежить від маринування, тоді можна створити піклер із приватною таблицею відправлення.

The global dispatch table managed by the copyreg module is available as copyreg.dispatch_table. Therefore, one may choose to use a modified copy of copyreg.dispatch_table as a private dispatch table.

Наприклад

f = io.BytesIO()
p = pickle.Pickler(f)
p.dispatch_table = copyreg.dispatch_table.copy()
p.dispatch_table[SomeClass] = reduce_SomeClass

створює екземпляр pickle.Pickler із приватною таблицею відправлення, яка спеціально обробляє клас SomeClass. Як альтернатива, код:

class MyPickler(pickle.Pickler):
    dispatch_table = copyreg.dispatch_table.copy()
    dispatch_table[SomeClass] = reduce_SomeClass
f = io.BytesIO()
p = MyPickler(f)

робить те саме, але всі екземпляри MyPickler за замовчуванням спільно використовуватимуть приватну таблицю відправлення. З іншого боку, код

copyreg.pickle(SomeClass, reduce_SomeClass)
f = io.BytesIO()
p = pickle.Pickler(f)

змінює глобальну таблицю відправлення, яку використовують усі користувачі модуля copyreg.

Обробка об’єктів із збереженням стану

Here’s an example that shows how to modify pickling behavior for a class. The TextReader class below opens a text file, and returns the line number and line contents each time its readline() method is called. If a TextReader instance is pickled, all attributes except the file object member are saved. When the instance is unpickled, the file is reopened, and reading resumes from the last location. The __setstate__() and __getstate__() methods are used to implement this behavior.

class TextReader:
    """Print and number lines in a text file."""

    def __init__(self, filename):
        self.filename = filename
        self.file = open(filename)
        self.lineno = 0

    def readline(self):
        self.lineno += 1
        line = self.file.readline()
        if not line:
            return None
        if line.endswith('\n'):
            line = line[:-1]
        return "%i: %s" % (self.lineno, line)

    def __getstate__(self):
        # Copy the object's state from self.__dict__ which contains
        # all our instance attributes. Always use the dict.copy()
        # method to avoid modifying the original state.
        state = self.__dict__.copy()
        # Remove the unpicklable entries.
        del state['file']
        return state

    def __setstate__(self, state):
        # Restore instance attributes (i.e., filename and lineno).
        self.__dict__.update(state)
        # Restore the previously opened file's state. To do so, we need to
        # reopen it and read from it until the line count is restored.
        file = open(self.filename)
        for _ in range(self.lineno):
            file.readline()
        # Finally, save the file.
        self.file = file

Зразок використання може бути приблизно таким:

>>> reader = TextReader("hello.txt")
>>> reader.readline()
'1: Hello world!'
>>> reader.readline()
'2: I am line number two.'
>>> new_reader = pickle.loads(pickle.dumps(reader))
>>> new_reader.readline()
'3: Goodbye!'

Спеціальне скорочення для типів, функцій та інших об’єктів

Нове в версії 3.8.

Іноді dispatch_table може бути недостатньо гнучким. Зокрема, ми можемо захотіти налаштувати маринування на основі іншого критерію, ніж тип об’єкта, або ми можемо захотіти налаштувати маринування функцій і класів.

For those cases, it is possible to subclass from the Pickler class and implement a reducer_override() method. This method can return an arbitrary reduction tuple (see __reduce__()). It can alternatively return NotImplemented to fallback to the traditional behavior.

Якщо визначено і dispatch_table, і reducer_override(), то метод reducer_override() має пріоритет.

Примітка

З міркувань продуктивності reducer_override() не можна викликати для таких об’єктів: None, True, False і точних екземплярів int , float, bytes, str, dict, set, frozenset, list і tuple.

Ось простий приклад, де ми дозволяємо маринувати та реконструювати заданий клас:

import io
import pickle

class MyClass:
    my_attribute = 1

class MyPickler(pickle.Pickler):
    def reducer_override(self, obj):
        """Custom reducer for MyClass."""
        if getattr(obj, "__name__", None) == "MyClass":
            return type, (obj.__name__, obj.__bases__,
                          {'my_attribute': obj.my_attribute})
        else:
            # For any other object, fallback to usual reduction
            return NotImplemented

f = io.BytesIO()
p = MyPickler(f)
p.dump(MyClass)

del MyClass

unpickled_class = pickle.loads(f.getvalue())

assert isinstance(unpickled_class, type)
assert unpickled_class.__name__ == "MyClass"
assert unpickled_class.my_attribute == 1

Позасмугові буфери

Нове в версії 3.8.

У деяких контекстах модуль pickle використовується для передачі величезних обсягів даних. Тому може бути важливо мінімізувати кількість копій пам’яті, щоб зберегти продуктивність і споживання ресурсів. Однак нормальна робота модуля pickle, оскільки він перетворює графоподібну структуру об’єктів у послідовний потік байтів, за своєю суттю передбачає копіювання даних до потоку pickle і з нього.

Це обмеження можна уникнути, якщо і провайдер (реалізація типів об’єктів, що передаються), і споживач (реалізація системи зв’язку) підтримують засоби позасмугової передачі, надані протоколом pickle 5 і вище.

API провайдера

The large data objects to be pickled must implement a __reduce_ex__() method specialized for protocol 5 and higher, which returns a PickleBuffer instance (instead of e.g. a bytes object) for any large data.

Об’єкт PickleBuffer сигналізує, що базовий буфер придатний для позасмугової передачі даних. Ці об’єкти залишаються сумісними зі звичайним використанням модуля pickle. Однак споживачі також можуть повідомити pickle, що вони самостійно оброблятимуть ці буфери.

Споживацький API

Система зв’язку може ввімкнути спеціальну обробку об’єктів PickleBuffer, створених під час серіалізації графа об’єктів.

На стороні надсилання йому потрібно передати аргумент buffer_callback до Pickler (або до функції dump() або dumps()), який буде викликано з кожним PickleBuffer, створений під час маринування графа об’єкта. Буфери, накопичені buffer_callback, не бачитимуть своїх даних, скопійованих у потік pickle, буде вставлено лише дешевий маркер.

На стороні приймача йому потрібно передати аргумент buffers до Unpickler (або до функції load() або loads()), який є ітерацією буферів, які були передається до buffer_callback. Ця ітерація повинна створювати буфери в тому ж порядку, в якому вони були передані buffer_callback. Ці буфери нададуть дані, очікувані реконструкторами об’єктів, маринування яких створило оригінальні об’єкти PickleBuffer.

Між відправною та приймальною сторонами система зв’язку може вільно реалізувати власний механізм передачі для позасмугових буферів. Потенційна оптимізація включає використання спільної пам’яті або залежне від типу даних стиснення.

приклад

Ось тривіальний приклад, у якому ми реалізуємо підклас bytearray, здатний брати участь у позаполосному відбиранні буфера:

class ZeroCopyByteArray(bytearray):

    def __reduce_ex__(self, protocol):
        if protocol >= 5:
            return type(self)._reconstruct, (PickleBuffer(self),), None
        else:
            # PickleBuffer is forbidden with pickle protocols <= 4.
            return type(self)._reconstruct, (bytearray(self),)

    @classmethod
    def _reconstruct(cls, obj):
        with memoryview(obj) as m:
            # Get a handle over the original buffer object
            obj = m.obj
            if type(obj) is cls:
                # Original buffer object is a ZeroCopyByteArray, return it
                # as-is.
                return obj
            else:
                return cls(obj)

Реконструктор (метод класу _reconstruct) повертає наданий об’єкт буфера, якщо він має правильний тип. Це простий спосіб імітувати поведінку без копіювання на цьому прикладі іграшки.

Зі сторони споживача ми можемо відібрати ці об’єкти звичайним способом, який після десеріалізації дасть нам копію оригінального об’єкта:

b = ZeroCopyByteArray(b"abc")
data = pickle.dumps(b, protocol=5)
new_b = pickle.loads(data)
print(b == new_b)  # True
print(b is new_b)  # False: a copy was made

Але якщо ми передаємо buffer_callback, а потім повертаємо накопичені буфери під час десеріалізації, ми можемо повернути вихідний об’єкт:

b = ZeroCopyByteArray(b"abc")
buffers = []
data = pickle.dumps(b, protocol=5, buffer_callback=buffers.append)
new_b = pickle.loads(data, buffers=buffers)
print(b == new_b)  # True
print(b is new_b)  # True: no copy was made

Цей приклад обмежений тим фактом, що bytearray виділяє власну пам’ять: ви не можете створити екземпляр bytearray, який підтримується пам’яттю іншого об’єкта. Однак сторонні типи даних, такі як масиви NumPy, не мають цього обмеження та дозволяють використовувати нульове копіювання (або створення якомога меншої кількості копій) під час передачі між окремими процесами чи системами.

Дивись також

PEP 574 – Протокол Pickle 5 із позаполосними даними

Обмеження Globals

За замовчуванням unpickling імпортує будь-який клас або функцію, знайдені в даних pickle. Для багатьох програм така поведінка є неприйнятною, оскільки вона дозволяє unpickler імпортувати та викликати довільний код. Просто подумайте, що робить цей створений вручну потік даних маринованих огірків під час завантаження:

>>> import pickle
>>> pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
hello world
0

У цьому прикладі unpickler імпортує функцію os.system(), а потім застосовує рядковий аргумент «echo hello world». Хоча цей приклад не образливий, неважко уявити такий, який може пошкодити вашу систему.

З цієї причини ви можете контролювати те, що буде видалено, налаштувавши Unpickler.find_class(). На відміну від назви, Unpickler.find_class() викликається щоразу, коли запитується глобал (тобто клас або функція). Таким чином, можна або повністю заборонити глобальні елементи, або обмежити їх безпечною підмножиною.

Ось приклад unpickler, який дозволяє завантажити лише декілька безпечних класів із модуля builtins:

import builtins
import io
import pickle

safe_builtins = {
    'range',
    'complex',
    'set',
    'frozenset',
    'slice',
}

class RestrictedUnpickler(pickle.Unpickler):

    def find_class(self, module, name):
        # Only allow safe classes from builtins.
        if module == "builtins" and name in safe_builtins:
            return getattr(builtins, name)
        # Forbid everything else.
        raise pickle.UnpicklingError("global '%s.%s' is forbidden" %
                                     (module, name))

def restricted_loads(s):
    """Helper function analogous to pickle.loads()."""
    return RestrictedUnpickler(io.BytesIO(s)).load()

Зразок використання нашого unpickler, що працює за призначенням:

>>> restricted_loads(pickle.dumps([1, 2, range(15)]))
[1, 2, range(0, 15)]
>>> restricted_loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
Traceback (most recent call last):
  ...
pickle.UnpicklingError: global 'os.system' is forbidden
>>> restricted_loads(b'cbuiltins\neval\n'
...                  b'(S\'getattr(__import__("os"), "system")'
...                  b'("echo hello world")\'\ntR.')
Traceback (most recent call last):
  ...
pickle.UnpicklingError: global 'builtins.eval' is forbidden

Як показують наші приклади, ви повинні бути обережними з тим, що ви дозволяєте не маринувати. Тому, якщо питання безпеки викликає занепокоєння, ви можете розглянути такі альтернативи, як маршалінговий API у xmlrpc.client або рішення сторонніх розробників.

Продуктивність

Останні версії протоколу pickle (від протоколу 2 і вище) містять ефективне двійкове кодування для кількох загальних функцій і вбудованих типів. Крім того, модуль pickle має прозорий оптимізатор, написаний мовою C.

Приклади

Для найпростішого коду використовуйте функції dump() і load().

import pickle

# An arbitrary collection of objects supported by pickle.
data = {
    'a': [1, 2.0, 3+4j],
    'b': ("character string", b"byte string"),
    'c': {None, True, False}
}

with open('data.pickle', 'wb') as f:
    # Pickle the 'data' dictionary using the highest protocol available.
    pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)

У наступному прикладі зчитуються результуючі мариновані дані.

import pickle

with open('data.pickle', 'rb') as f:
    # The protocol version used is detected automatically, so we do not
    # have to specify it.
    data = pickle.load(f)

Дивись також

Модуль copyreg

Реєстрація конструктора інтерфейсу Pickle для типів розширень.

Модуль pickletools

Інструменти для роботи з маринованими даними та їх аналізу.

Модуль shelve

Індексовані бази даних об’єктів; використовує pickle.

Модуль copy

Неглибоке і глибоке копіювання об’єктів.

Модуль marshal

Високопродуктивна серіалізація вбудованих типів.

Виноски