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 (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 fromPickleError
.Зверніться до Що можна маринувати і не квашети?, щоб дізнатися, які види об’єктів можна маринувати.
- 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 asNone
), 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 isNone
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()
, не може мати постійний ідентифікатор.Перегляньте Постійність зовнішніх об’єктів для деталей та прикладів використання.
Змінено в версії 3.13: Add the default implementation of this method in the C implementation of
Pickler
.
- 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
, тоді він використовуватиметься як типова таблиця відправлення для екземплярів цього класу.Перегляньте Таблиці відправлення приклади використання.
Added in version 3.3.
- reducer_override(obj)¶
Special reducer that can be defined in
Pickler
subclasses. This method has priority over any reducer in thedispatch_table
. It should conform to the same interface as a__reduce__()
method, and can optionally returnNotImplemented
to fallback ondispatch_table
-registered reducers to pickleobj
.Детальний приклад див. Спеціальне скорочення для типів, функцій та інших об’єктів.
Added in version 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 wasNone
when aPickler
was instantiated (or whendump()
ordumps()
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
.Перегляньте Постійність зовнішніх об’єктів для деталей та прикладів використання.
Змінено в версії 3.13: Add the default implementation of this method in the C implementation of
Unpickler
.
- 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 або вище. Вони підходять для позасмугової серіалізації.Added in version 3.8.
- raw()¶
Повертає
memoryview
області пам’яті, що лежить в основі цього буфера. Повернений об’єкт є одновимірним C-суміжним представленням пам’яті у форматіB
(байти без знаку).BufferError
виникає, якщо буфер не суміжний ні на C, ні на Fortran.
- release()¶
Вивільніть базовий буфер, відкритий об’єктом PickleBuffer.
Що можна маринувати і не квашети?¶
Маринувати можна такі види:
built-in constants (
None
,True
,False
,Ellipsis
, andNotImplemented
);цілі числа, числа з плаваючою комою, комплексні числа;
рядки, байти, байтові масиви;
кортежі, списки, набори та словники, що містять лише об’єкти, які можна вибирати;
функції (вбудовані та визначені користувачем), доступні з верхнього рівня модуля (за допомогою
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 isNone
.For a class that has an instance
__dict__
and no__slots__
, the default state isself.__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 isNone
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 theobject
class.
- object.__setstate__(state)¶
Якщо під час розбирання клас визначає
__setstate__()
, він викликається зі станом unpickled. У цьому випадку немає вимоги, щоб об’єкт стану був словником. В іншому випадку маринований стан має бути словником, а його елементи призначаються словнику нового екземпляра.Примітка
If
__reduce__()
returns a state with valueNone
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, usingobj.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. (Whetherappend()
orextend()
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__()
.Added in version 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!'
Спеціальне скорочення для типів, функцій та інших об’єктів¶
Added in version 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
Позасмугові буфери¶
Added in version 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
Високопродуктивна серіалізація вбудованих типів.
Виноски