pickle --- Tuần tự hóa đối tượng Python

Source code: Lib/pickle.py


The pickle module implements binary protocols for serializing and de-serializing a Python object structure. "Pickling" is the process whereby a Python object hierarchy is converted into a byte stream, and "unpickling" is the inverse operation, whereby a byte stream (from a binary file or bytes-like object) is converted back into an object hierarchy. Pickling (and unpickling) is alternatively known as "serialization", "marshalling," [1] or "flattening"; tuy nhiên, để tránh nhầm lẫn, các thuật ngữ được sử dụng ở đây là "ngâm chua" và "tháo dưa chua".

Cảnh báo

Mô-đun pickle is not secure. Chỉ giải nén dữ liệu mà bạn tin tưởng.

Có thể xây dựng dữ liệu dưa chua độc hại sẽ execute arbitrary code during unpickling. Never unpickle data that could have come from an untrusted source, or that could have been tampered with.

Consider signing data with hmac if you need to ensure that it has not been tampered with.

Safer serialization formats such as json may be more appropriate if you are processing untrusted data. Xem So sánh với json.

Mối quan hệ với các mô-đun Python khác

So sánh với marshal

Python has a more primitive serialization module called marshal, but in general pickle should always be the preferred way to serialize Python objects. marshal tồn tại chủ yếu để hỗ trợ các tệp .pyc của Python.

The pickle module differs from marshal in several significant ways:

  • marshal cannot be used to serialize user-defined classes and their instances. pickle can save and restore class instances transparently, however the class definition must be importable and live in the same module as when the object was stored.

  • The marshal serialization format is not guaranteed to be portable across Python versions. Because its primary job in life is to support .pyc files, the Python implementers reserve the right to change the serialization format in non-backwards compatible ways should the need arise. The pickle serialization format is guaranteed to be backwards compatible across Python releases provided a compatible pickle protocol is chosen and pickling and unpickling code deals with Python 2 to Python 3 type differences if your data is crossing that unique breaking change language boundary.

So sánh với json

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

  • JSON is a text serialization format (it outputs unicode text, although most of the time it is then encoded to utf-8), while pickle is a binary serialization format;

  • JSON con người có thể đọc được, còn dưa chua thì không;

  • JSON is interoperable and widely used outside of the Python ecosystem, while pickle is Python-specific;

  • JSON, by default, can only represent a subset of the Python built-in types, and no custom classes; pickle can represent an extremely large number of Python types (many of them automatically, by clever usage of Python's introspection facilities; complex cases can be tackled by implementing specific object APIs);

  • Unlike pickle, deserializing untrusted JSON does not in itself create an arbitrary code execution vulnerability.

Xem thêm

Mô-đun json: mô-đun thư viện tiêu chuẩn cho phép tuần tự hóa và giải tuần tự hóa JSON.

Định dạng luồng dữ liệu

Định dạng dữ liệu được pickle sử dụng là dành riêng cho Python. Điều này có ưu điểm là không có hạn chế nào được áp đặt bởi các tiêu chuẩn bên ngoài như JSON (không thể biểu thị việc chia sẻ con trỏ); tuy nhiên điều đó có nghĩa là các chương trình không phải Python có thể không có khả năng tái tạo lại các đối tượng Python đã được chọn.

Theo mặc định, định dạng dữ liệu pickle sử dụng biểu diễn nhị phân tương đối nhỏ gọn. Nếu bạn cần các đặc tính kích thước tối ưu, bạn có thể compress dữ liệu được chọn một cách hiệu quả.

Mô-đun pickletools chứa các công cụ để phân tích luồng dữ liệu do pickle tạo ra. Mã nguồn pickletools có nhiều nhận xét sâu sắc về các mã hoạt động được sử dụng bởi các giao thức dưa chua.

Hiện tại có 6 quy trình khác nhau có thể được sử dụng để ngâm chua. Giao thức được sử dụng càng cao thì phiên bản Python càng cần thiết để đọc phần mềm được tạo ra.

  • Giao thức phiên bản 0 là giao thức "dễ đọc" ban đầu và tương thích ngược với các phiên bản Python trước đó.

  • Giao thức phiên bản 1 là định dạng nhị phân cũ cũng tương thích với các phiên bản Python trước đó.

  • Giao thức phiên bản 2 được giới thiệu trong Python 2.3. Nó cung cấp khả năng tẩy new-style classes hiệu quả hơn nhiều. Tham khảo PEP 307 để biết thông tin về những cải tiến do giao thức 2 mang lại.

  • Giao thức phiên bản 3 đã được thêm vào Python 3.0. Nó có hỗ trợ rõ ràng cho các đối tượng bytes và Python 2.x không thể giải nén được. Đây là giao thức mặc định trong Python 3.0--3.7.

  • Giao thức phiên bản 4 đã được thêm vào Python 3.4. Nó bổ sung thêm hỗ trợ cho các đối tượng rất lớn, chọn lọc nhiều loại đối tượng hơn và một số tối ưu hóa định dạng dữ liệu. Đây là giao thức mặc định trong Python 3.8--3.13. Tham khảo PEP 3154 để biết thông tin về những cải tiến do giao thức 4 mang lại.

  • Giao thức phiên bản 5 đã được thêm vào Python 3.8. Nó bổ sung thêm hỗ trợ cho dữ liệu ngoài băng tần và tăng tốc cho dữ liệu trong băng tần. Đây là giao thức mặc định bắt đầu bằng Python 3.14. Tham khảo PEP 574 để biết thông tin về những cải tiến do giao thức 5 mang lại.

Ghi chú

Tuần tự hóa là một khái niệm nguyên thủy hơn là tính bền bỉ; mặc dù pickle đọc và ghi các đối tượng tệp, nhưng nó không xử lý vấn đề đặt tên các đối tượng liên tục cũng như vấn đề (thậm chí còn phức tạp hơn) về quyền truy cập đồng thời vào các đối tượng liên tục. Mô-đun pickle có thể chuyển đổi một đối tượng phức tạp thành luồng byte và nó có thể chuyển đổi luồng byte thành một đối tượng có cùng cấu trúc bên trong. Có lẽ điều rõ ràng nhất cần làm với các luồng byte này là ghi chúng vào một tệp, nhưng cũng có thể gửi chúng qua mạng hoặc lưu trữ chúng trong cơ sở dữ liệu. Mô-đun shelve cung cấp một giao diện đơn giản để chọn và giải nén các đối tượng trên các tệp cơ sở dữ liệu kiểu DBM.

Giao diện mô-đun

Để tuần tự hóa hệ thống phân cấp đối tượng, bạn chỉ cần gọi hàm dumps(). Tương tự, để hủy tuần tự hóa luồng dữ liệu, bạn gọi hàm loads(). Tuy nhiên, nếu bạn muốn kiểm soát nhiều hơn việc tuần tự hóa và hủy tuần tự hóa, bạn có thể tạo một đối tượng Pickler hoặc Unpickler tương ứng.

Mô-đun pickle cung cấp các hằng số sau:

pickle.HIGHEST_PROTOCOL

Một số nguyên, protocol version cao nhất hiện có. Giá trị này có thể được chuyển dưới dạng giá trị protocol cho các hàm dump()dumps() cũng như hàm tạo Pickler.

pickle.DEFAULT_PROTOCOL

Một số nguyên, protocol version mặc định được sử dụng để ngâm chua. Có thể ít hơn HIGHEST_PROTOCOL. Hiện tại giao thức mặc định là 5, được giới thiệu trong Python 3.8 và không tương thích với các phiên bản trước. Phiên bản này giới thiệu hỗ trợ cho bộ đệm ngoài băng tần, trong đó dữ liệu tương thích với PEP 3118 có thể được truyền riêng biệt khỏi luồng dưa chua chính.

Thay đổi trong phiên bản 3.0: Giao thức mặc định là 3.

Thay đổi trong phiên bản 3.8: Giao thức mặc định là 4.

Thay đổi trong phiên bản 3.14: Giao thức mặc định là 5.

Mô-đun pickle cung cấp các chức năng sau để giúp quá trình tẩy thuận tiện hơn:

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

Viết biểu diễn ngâm của đối tượng obj vào file object file đang mở. Điều này tương đương với Pickler(file, protocol).dump(obj).

Các đối số file, protocol, fix_importsbuffer_callback có cùng ý nghĩa như trong hàm tạo Pickler.

Thay đổi trong phiên bản 3.8: Đối số buffer_callback đã được thêm vào.

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

Trả về biểu diễn đã được chọn lọc của đối tượng obj dưới dạng đối tượng bytes, thay vì ghi nó vào một tệp.

Các đối số protocol, fix_importsbuffer_callback có cùng ý nghĩa như trong hàm tạo Pickler.

Thay đổi trong phiên bản 3.8: Đối số buffer_callback đã được thêm vào.

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

Đọc biểu diễn được chọn lọc của một đối tượng từ file object file mở và trả về hệ thống phân cấp đối tượng được hoàn nguyên được chỉ định trong đó. Điều này tương đương với Unpickler(file).load().

Phiên bản giao thức của dưa chua được phát hiện tự động, do đó không cần đối số giao thức. Các byte qua biểu diễn đã được chọn của đối tượng sẽ bị bỏ qua.

Các đối số file, fix_imports, encoding, errors, strictbuffers có cùng ý nghĩa như trong hàm tạo Unpickler.

Thay đổi trong phiên bản 3.8: Đối số buffers đã được thêm vào.

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

Trả về hệ thống phân cấp đối tượng được hoàn nguyên của biểu diễn data được chọn lọc của một đối tượng. data phải là bytes-like object.

Phiên bản giao thức của dưa chua được phát hiện tự động, do đó không cần đối số giao thức. Các byte qua biểu diễn đã được chọn của đối tượng sẽ bị bỏ qua.

Các đối số fix_imports, encoding, errors, strictbuffers có cùng ý nghĩa như trong hàm tạo Unpickler.

Thay đổi trong phiên bản 3.8: Đối số buffers đã được thêm vào.

Mô-đun pickle xác định ba trường hợp ngoại lệ:

exception pickle.PickleError

Lớp cơ sở chung cho các trường hợp ngoại lệ ngâm chua khác. Nó kế thừa từ Exception.

exception pickle.PicklingError

Lỗi xảy ra khi Pickler gặp phải một đối tượng không thể chọn được. Nó kế thừa từ PickleError.

Hãy tham khảo Những gì có thể được ngâm và không được ngâm? để biết những loại đồ vật nào có thể ngâm được.

exception pickle.UnpicklingError

Lỗi xảy ra khi xảy ra sự cố khi giải nén một đối tượng, chẳng hạn như hỏng dữ liệu hoặc vi phạm bảo mật. Nó kế thừa từ PickleError.

Lưu ý rằng các trường hợp ngoại lệ khác cũng có thể xuất hiện trong quá trình giải nén, bao gồm (nhưng không nhất thiết giới hạn ở) AttributionError, EOFError, ImportError và IndexError.

Mô-đun pickle xuất ba lớp, Pickler, UnpicklerPickleBuffer:

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

Việc này cần một tệp nhị phân để ghi luồng dữ liệu dưa chua.

Đối số protocol tùy chọn, một số nguyên, yêu cầu bộ chọn sử dụng giao thức đã cho; các giao thức được hỗ trợ là từ 0 đến HIGHEST_PROTOCOL. Nếu không được chỉ định, mặc định là DEFAULT_PROTOCOL. Nếu số âm được chỉ định, HIGHEST_PROTOCOL sẽ được chọn.

Đối số file phải có phương thức write() chấp nhận đối số byte đơn. Do đó, nó có thể là một tệp trên đĩa được mở để ghi nhị phân, phiên bản io.BytesIO hoặc bất kỳ đối tượng tùy chỉnh nào khác đáp ứng giao diện này.

Nếu fix_imports là đúng và protocol nhỏ hơn 3, thì dưa sẽ cố gắng ánh xạ tên Python 3 mới với tên mô-đun cũ được sử dụng trong Python 2, để luồng dữ liệu dưa có thể đọc được bằng Python 2.

Nếu buffer_callbackNone (mặc định), các chế độ xem bộ đệm sẽ được tuần tự hóa thành file như một phần của luồng dưa chua.

Nếu buffer_callback không phải là None thì nó có thể được gọi bao nhiêu lần tùy thích với chế độ xem bộ đệm. Nếu lệnh gọi lại trả về giá trị sai (chẳng hạn như None), bộ đệm đã cho là out-of-band; mặt khác, bộ đệm được tuần tự hóa trong băng tần, tức là bên trong luồng dưa chua.

Sẽ là lỗi nếu buffer_callback không phải là NoneprotocolNone hoặc nhỏ hơn 5.

Thay đổi trong phiên bản 3.8: Đối số buffer_callback đã được thêm vào.

dump(obj)

Viết biểu diễn được chọn của obj vào đối tượng tệp đang mở được cung cấp trong hàm tạo.

persistent_id(obj)

Không làm gì theo mặc định. Điều này tồn tại để một lớp con có thể ghi đè lên nó.

Nếu persistent_id() trả về None, obj sẽ được ngâm như bình thường. Bất kỳ giá trị nào khác khiến Pickler phát ra giá trị được trả về dưới dạng ID cố định cho obj. Ý nghĩa của ID liên tục này phải được xác định bởi Unpickler.persistent_load(). Lưu ý rằng giá trị được trả về bởi persistent_id() không thể có ID cố định.

Xem Sự kiên trì của các đối tượng bên ngoài để biết chi tiết và ví dụ về cách sử dụng.

Thay đổi trong phiên bản 3.13: Thêm cách triển khai mặc định của phương pháp này trong quá trình triển khai C của Pickler.

dispatch_table

Bảng điều phối của đối tượng pickler là một sổ đăng ký reduction functions thuộc loại có thể được khai báo bằng copyreg.pickle(). Nó là một ánh xạ có khóa là các lớp và có giá trị là các hàm rút gọn. Hàm rút gọn lấy một đối số duy nhất của lớp được liên kết và phải tuân theo cùng giao diện với phương thức __reduce__().

Theo mặc định, đối tượng pickler sẽ không có thuộc tính dispatch_table và thay vào đó, nó sẽ sử dụng bảng điều phối chung do mô-đun copyreg quản lý. Tuy nhiên, để tùy chỉnh quá trình tẩy cho một đối tượng bộ chọn cụ thể, người ta có thể đặt thuộc tính dispatch_table thành một đối tượng giống như dict. Ngoài ra, nếu một lớp con của Pickler có thuộc tính dispatch_table thì thuộc tính này sẽ được sử dụng làm bảng điều phối mặc định cho các phiên bản của lớp đó.

Xem Bảng điều phối để biết ví dụ sử dụng.

Added in version 3.3.

reducer_override(obj)

Bộ giảm tốc đặc biệt có thể được định nghĩa trong các lớp con Pickler. Phương pháp này được ưu tiên hơn bất kỳ bộ giảm tốc nào trong dispatch_table. Nó phải tuân theo cùng một giao diện với phương thức __reduce__() và có thể tùy ý trả về NotImplemented để dự phòng trên các bộ giảm tốc đã đăng ký dispatch_table để chọn obj.

Để biết ví dụ chi tiết, hãy xem Giảm tùy chỉnh cho các loại, chức năng và các đối tượng khác.

Added in version 3.8.

fast

Không dùng nữa. Bật chế độ nhanh nếu được đặt thành giá trị thực. Chế độ nhanh sẽ vô hiệu hóa việc sử dụng bản ghi nhớ, do đó đẩy nhanh quá trình tẩy bằng cách không tạo ra các mã opcode PUT thừa. Không nên sử dụng nó với các đối tượng tự tham chiếu, nếu không sẽ khiến Pickler tái diễn vô hạn.

Sử dụng pickletools.optimize() nếu bạn cần dưa chua nhỏ gọn hơn.

clear_memo()

Xóa "bản ghi nhớ" của người nhặt dưa.

Bản ghi nhớ là cấu trúc dữ liệu ghi nhớ những đối tượng mà bộ chọn đã thấy, do đó các đối tượng dùng chung hoặc đệ quy được chọn theo tham chiếu chứ không phải theo giá trị. Phương pháp này rất hữu ích khi sử dụng lại dưa chua.

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

Việc này cần một tệp nhị phân để đọc luồng dữ liệu dưa chua.

Phiên bản giao thức của dưa chua được phát hiện tự động, do đó không cần đối số giao thức.

Đối số file phải có ba phương thức, một phương thức read() nhận đối số nguyên, một phương thức readinto() nhận đối số bộ đệm và một phương thức readline() không yêu cầu đối số, như trong giao diện io.BufferedIOBase. Do đó, file có thể là một tệp trên đĩa được mở để đọc nhị phân, đối tượng io.BytesIO hoặc bất kỳ đối tượng tùy chỉnh nào khác đáp ứng giao diện này.

Các đối số tùy chọn fix_imports, encodingerrors được sử dụng để kiểm soát hỗ trợ tương thích cho luồng dưa chua do Python 2 tạo ra. Nếu fix_imports là đúng, dưa chua sẽ cố gắng ánh xạ tên Python 2 cũ sang tên mới được sử dụng trong Python 3. encodingerrors cho Picker biết cách giải mã các phiên bản chuỗi 8 bit được chọn bởi Python 2; những giá trị mặc định này lần lượt là 'ASCII' và 'strict'. Zz010zz có thể là 'byte' để đọc các phiên bản chuỗi 8 bit này dưới dạng đối tượng byte. Cần phải sử dụng encoding='latin1' để giải nén các mảng NumPy và các phiên bản của datetime, datetime được chọn bởi Python 2.

Nếu buffersNone (mặc định), thì tất cả dữ liệu cần thiết cho quá trình khử lưu lượng phải được chứa trong luồng dưa chua. Điều này có nghĩa là đối số buffer_callbackNone khi Pickler được khởi tạo (hoặc khi dump() hoặc dumps() được gọi).

Nếu buffers không phải là None, thì nó sẽ là một đối tượng có thể lặp lại được hỗ trợ bộ đệm được sử dụng mỗi khi luồng dưa tham chiếu chế độ xem bộ đệm out-of-band. Các bộ đệm như vậy đã được cung cấp cho buffer_callback của đối tượng Pickler.

Thay đổi trong phiên bản 3.8: Đối số buffers đã được thêm vào.

load()

Đọc biểu diễn được chọn lọc của một đối tượng từ đối tượng tệp đang mở được cung cấp trong hàm tạo và trả về hệ thống phân cấp đối tượng được hoàn nguyên được chỉ định trong đó. Các byte qua biểu diễn đã được chọn của đối tượng sẽ bị bỏ qua.

persistent_load(pid)

Tăng UnpicklingError theo mặc định.

Nếu được xác định, persistent_load() sẽ trả về đối tượng được chỉ định bởi ID liên tục pid. Nếu gặp phải ID liên tục không hợp lệ, UnpicklingError sẽ được nâng lên.

Xem Sự kiên trì của các đối tượng bên ngoài để biết chi tiết và ví dụ về cách sử dụng.

Thay đổi trong phiên bản 3.13: Thêm cách triển khai mặc định của phương pháp này trong quá trình triển khai C của Unpickler.

find_class(module, name)

Nhập module nếu cần và trả về đối tượng có tên name từ đối tượng đó, trong đó các đối số modulename là các đối tượng str. Lưu ý, không giống như tên gọi của nó, find_class() cũng được sử dụng để tìm kiếm các hàm.

Các lớp con có thể ghi đè điều này để giành quyền kiểm soát loại đối tượng và cách chúng có thể được tải, có khả năng giảm rủi ro bảo mật. Tham khảo Hạn chế toàn cầu để biết chi tiết.

Tăng một auditing event pickle.find_class với các đối số module, name.

class pickle.PickleBuffer(buffer)

Trình bao bọc cho bộ đệm biểu thị dữ liệu có thể chọn được. buffer phải là đối tượng buffer-providing, chẳng hạn như bytes-like object hoặc mảng N chiều.

PickleBuffer bản thân nó là một nhà cung cấp bộ đệm, do đó có thể chuyển nó sang các API khác mong đợi một đối tượng cung cấp bộ đệm, chẳng hạn như memoryview.

Các đối tượng PickleBuffer chỉ có thể được tuần tự hóa bằng giao thức dưa 5 trở lên. Họ đủ điều kiện cho out-of-band serialization.

Added in version 3.8.

raw()

Trả về memoryview của vùng bộ nhớ bên dưới bộ đệm này. Đối tượng được trả về là chế độ xem bộ nhớ liền kề C một chiều với định dạng B (byte không dấu). BufferError được tăng lên nếu bộ đệm không liền kề C- hay Fortran.

release()

Giải phóng bộ đệm cơ bản do đối tượng PickleBuffer hiển thị.

Những gì có thể được ngâm và không được ngâm?

Các loại sau đây có thể được ngâm:

  • các hằng số tích hợp (None, True, False, EllipsisNotImplemented);

  • số nguyên, số dấu phẩy động, số phức;

  • chuỗi, byte, bytearray;

  • bộ dữ liệu, danh sách, bộ và từ điển chỉ chứa các đối tượng có thể chọn được;

  • các chức năng (tích hợp sẵn và do người dùng xác định) có thể truy cập được từ cấp cao nhất của mô-đun (sử dụng def, không phải lambda);

  • các lớp có thể truy cập được từ cấp cao nhất của mô-đun;

  • các trường hợp của các lớp như vậy mà kết quả của việc gọi __getstate__() có thể được chọn (xem phần Phiên bản lớp Pickling để biết chi tiết).

Nỗ lực chọn các đối tượng không thể chọn được sẽ gây ra ngoại lệ PicklingError; khi điều này xảy ra, một số byte không xác định có thể đã được ghi vào tệp cơ bản. Việc cố gắng chọn cấu trúc dữ liệu có tính đệ quy cao có thể vượt quá độ sâu đệ quy tối đa, RecursionError sẽ được nâng lên trong trường hợp này. Bạn có thể cẩn thận nâng cao giới hạn này bằng sys.setrecursionlimit().

Lưu ý rằng các hàm (tích hợp sẵn và do người dùng xác định) được chọn theo qualified name hoàn toàn chứ không phải theo giá trị. [2] Điều này có nghĩa là chỉ có tên hàm được chọn, cùng với tên của mô-đun chứa và các lớp. Cả mã của hàm cũng như bất kỳ thuộc tính hàm nào của nó đều không được chọn. Do đó, mô-đun xác định phải có thể nhập được trong môi trường giải nén và mô-đun phải chứa đối tượng được đặt tên, nếu không sẽ xuất hiện ngoại lệ. [3]

Tương tự, các lớp được chọn theo tên đủ điều kiện, do đó, các hạn chế tương tự trong môi trường giải nén cũng được áp dụng. Lưu ý rằng không có mã hoặc dữ liệu nào của lớp được chọn, vì vậy trong ví dụ sau, thuộc tính lớp attr không được khôi phục trong môi trường giải nén:

lớp Foo:
    attr = 'Thuộc tính lớp'

dưa chua = dưa chua.dumps(Foo)

Những hạn chế này là lý do tại sao các hàm và lớp có thể chọn phải được xác định ở cấp cao nhất của mô-đun.

Tương tự, khi các thể hiện của lớp được chọn, mã và dữ liệu của lớp đó sẽ không được chọn cùng với chúng. Chỉ có dữ liệu cá thể được chọn. Việc này được thực hiện có mục đích, vì vậy bạn có thể sửa lỗi trong một lớp hoặc thêm phương thức vào lớp đó mà vẫn tải các đối tượng được tạo bằng phiên bản cũ hơn của lớp đó. Nếu bạn dự định có các đối tượng tồn tại lâu dài sẽ thấy nhiều phiên bản của một lớp, thì có thể đáng để đặt số phiên bản vào các đối tượng để có thể thực hiện các chuyển đổi phù hợp bằng phương pháp __setstate__() của lớp.

Phiên bản lớp Pickling

Trong phần này, chúng tôi mô tả các cơ chế chung có sẵn cho bạn để xác định, tùy chỉnh và kiểm soát cách chọn và bỏ chọn các phiên bản lớp.

Trong hầu hết các trường hợp, không cần mã bổ sung để làm cho các phiên bản có thể chọn được. Theo mặc định, Pickle sẽ truy xuất lớp và thuộc tính của một thể hiện thông qua việc xem xét nội tâm. Khi một thể hiện của lớp không được chọn, phương thức __init__() của nó thường được gọi là not. Hành vi mặc định trước tiên tạo một phiên bản chưa được khởi tạo, sau đó khôi phục các thuộc tính đã lưu. Đoạn mã sau đây cho thấy việc triển khai hành vi này:

lưu chắc chắn(obj):
    trả về (obj.__class__, obj.__dict__)

khôi phục def (cls, thuộc tính):
    obj = cls.__new__(cls)
    obj.__dict__.update(thuộc tính)
    trả lại đối tượng

Các lớp có thể thay đổi hành vi mặc định bằng cách cung cấp một hoặc một số phương thức đặc biệt:

object.__getnewargs_ex__()

Trong các giao thức 2 trở lên, các lớp triển khai phương thức __getnewargs_ex__() có thể ra lệnh cho các giá trị được truyền cho phương thức __new__() khi giải nén. Phương thức này phải trả về một cặp (args, kwargs) trong đó args là một bộ đối số vị trí và kwargs là một từ điển gồm các đối số được đặt tên để xây dựng đối tượng. Những thứ đó sẽ được chuyển sang phương thức __new__() sau khi giải nén.

Bạn nên triển khai phương thức này nếu phương thức __new__() của lớp bạn yêu cầu các đối số chỉ có từ khóa. Mặt khác, nên triển khai __getnewargs__() để tương thích.

Thay đổi trong phiên bản 3.6: __getnewargs_ex__() hiện được sử dụng trong giao thức 2 và 3.

object.__getnewargs__()

Phương thức này phục vụ mục đích tương tự như __getnewargs_ex__() nhưng chỉ hỗ trợ các đối số vị trí. Nó phải trả về một bộ đối số args sẽ được chuyển cho phương thức __new__() sau khi giải nén.

__getnewargs__() sẽ không được gọi nếu __getnewargs_ex__() được xác định.

Thay đổi trong phiên bản 3.6: Trước Python 3.6, __getnewargs__() được gọi thay vì __getnewargs_ex__() trong giao thức 2 và 3.

object.__getstate__()

Các lớp có thể ảnh hưởng hơn nữa đến cách các phiên bản của chúng được chọn bằng cách ghi đè phương thức __getstate__(). Nó được gọi và đối tượng trả về được chọn làm nội dung cho phiên bản, thay vì trạng thái mặc định. Có một số trường hợp:

  • Đối với một lớp không có phiên bản __dict__ và không có __slots__, trạng thái mặc định là None.

  • Đối với lớp có phiên bản __dict__ và không có __slots__, trạng thái mặc định là self.__dict__.

  • Đối với một lớp có một phiên bản __dict____slots__, trạng thái mặc định là một bộ dữ liệu bao gồm hai từ điển: self.__dict__ và một từ điển ánh xạ tên vị trí thành các giá trị vị trí. Chỉ các vị trí có giá trị mới được bao gồm trong phần sau.

  • Đối với một lớp có __slots__ và không có phiên bản __dict__, trạng thái mặc định là một bộ có mục đầu tiên là None và mục thứ hai là từ điển ánh xạ tên vị trí tới các giá trị vị trí được mô tả trong dấu đầu dòng trước đó.

Thay đổi trong phiên bản 3.11: Đã thêm cách triển khai mặc định của phương thức __getstate__() trong lớp object.

object.__setstate__(state)

Sau khi giải nén, nếu lớp định nghĩa __setstate__(), nó sẽ được gọi với trạng thái chưa được giải nén. Trong trường hợp đó, không có yêu cầu đối tượng trạng thái phải là từ điển. Nếu không, trạng thái được chọn phải là một từ điển và các mục của nó được gán cho từ điển của phiên bản mới.

Ghi chú

Nếu __reduce__() trả về trạng thái có giá trị None khi tẩy, phương thức __setstate__() sẽ không được gọi khi giải nén.

Tham khảo phần Xử lý các đối tượng có trạng thái để biết thêm thông tin về cách sử dụng các phương pháp __getstate__()__setstate__().

Ghi chú

Tại thời điểm giải nén, một số phương thức như __getattr__(), __getattribute__() hoặc __setattr__() có thể được gọi trên phiên bản. Trong trường hợp các phương thức đó dựa vào một số bất biến nội bộ là đúng, thì loại nên triển khai __new__() để thiết lập một bất biến đó, vì __init__() không được gọi khi giải nén một phiên bản.

Như chúng ta sẽ thấy, dưa chua không sử dụng trực tiếp các phương pháp được mô tả ở trên. Trên thực tế, các phương thức này là một phần của giao thức sao chép triển khai phương thức đặc biệt __reduce__(). Giao thức sao chép cung cấp một giao diện thống nhất để truy xuất dữ liệu cần thiết cho việc chọn lọc và sao chép các đối tượng. [4]

Mặc dù mạnh mẽ nhưng việc triển khai __reduce__() trực tiếp trong lớp của bạn dễ xảy ra lỗi. Vì lý do này, người thiết kế lớp nên sử dụng giao diện cấp cao (tức là __getnewargs_ex__(), __getstate__()__setstate__()) bất cứ khi nào có thể. Tuy nhiên, chúng tôi sẽ chỉ ra các trường hợp sử dụng __reduce__() là lựa chọn duy nhất hoặc dẫn đến việc ngâm chua hiệu quả hơn hoặc cả hai.

object.__reduce__()

Giao diện hiện được xác định như sau. Phương thức __reduce__() không có đối số và sẽ trả về một chuỗi hoặc tốt nhất là một bộ dữ liệu (đối tượng được trả về thường được gọi là "giá trị rút gọn").

Nếu một chuỗi được trả về, chuỗi đó sẽ được hiểu là tên của một biến toàn cục. Nó phải là tên cục bộ của đối tượng liên quan đến mô-đun của nó; mô-đun dưa tìm kiếm không gian tên mô-đun để xác định mô-đun của đối tượng. Hành vi này thường hữu ích cho những người độc thân.

Khi một bộ dữ liệu được trả về, nó phải dài từ hai đến sáu mục. Các mục tùy chọn có thể được bỏ qua hoặc None có thể được cung cấp làm giá trị của chúng. Ngữ nghĩa của từng mục theo thứ tự:

  • Một đối tượng có thể gọi được sẽ được gọi để tạo phiên bản ban đầu của đối tượng.

  • Một bộ đối số cho đối tượng có thể gọi được. Một bộ dữ liệu trống phải được cung cấp nếu đối tượng có thể gọi được không chấp nhận bất kỳ đối số nào.

  • Tùy chọn, trạng thái của đối tượng sẽ được chuyển tới phương thức __setstate__() của đối tượng như được mô tả trước đây. Nếu đối tượng không có phương thức như vậy thì giá trị phải là từ điển và nó sẽ được thêm vào thuộc tính __dict__ của đối tượng.

  • Tùy chọn, một trình vòng lặp (chứ không phải một chuỗi) mang lại các mục liên tiếp. Các mục này sẽ được thêm vào đối tượng bằng cách sử dụng obj.append(item) hoặc theo đợt bằng cách sử dụng obj.extend(list_of_items). Điều này chủ yếu được sử dụng cho các lớp con danh sách, nhưng có thể được các lớp khác sử dụng miễn là chúng có các phương thức append()extend() với chữ ký thích hợp. (Việc sử dụng append() hay extend() tùy thuộc vào phiên bản giao thức dưa chua nào được sử dụng cũng như số lượng mục cần thêm, vì vậy cả hai đều phải được hỗ trợ.)

  • Tùy chọn, một trình vòng lặp (không phải một chuỗi) tạo ra các cặp khóa-giá trị liên tiếp. Các mục này sẽ được lưu trữ vào đối tượng bằng obj[key] = value. Điều này chủ yếu được sử dụng cho các lớp con từ điển, nhưng có thể được các lớp khác sử dụng miễn là chúng triển khai __setitem__().

  • Tùy chọn, một lệnh gọi có chữ ký (obj, state). Lệnh gọi này cho phép người dùng kiểm soát theo chương trình hành vi cập nhật trạng thái của một đối tượng cụ thể, thay vì sử dụng phương thức __setstate__() tĩnh của obj. Nếu không phải None, lệnh gọi này sẽ được ưu tiên hơn __setstate__() của obj.

    Added in version 3.8: Mục bộ dữ liệu thứ sáu tùy chọn, (obj, state), đã được thêm vào.

object.__reduce_ex__(protocol)

Ngoài ra, phương pháp __reduce_ex__() có thể được xác định. Sự khác biệt duy nhất là phương thức này sẽ lấy một đối số nguyên duy nhất, phiên bản giao thức. Khi được xác định, dưa chua sẽ thích nó hơn phương thức __reduce__(). Ngoài ra, __reduce__() tự động trở thành từ đồng nghĩa với phiên bản mở rộng. Công dụng chính của phương pháp này là cung cấp các giá trị rút gọn tương thích ngược cho các bản phát hành Python cũ hơn.

Sự kiên trì của các đối tượng bên ngoài

Vì lợi ích của việc duy trì đối tượng, mô-đun pickle hỗ trợ khái niệm tham chiếu đến một đối tượng bên ngoài luồng dữ liệu được chọn. Các đối tượng như vậy được tham chiếu bằng ID cố định, ID này phải là một chuỗi ký tự chữ và số (đối với giao thức 0) [5] hoặc chỉ là một đối tượng tùy ý (đối với bất kỳ giao thức mới hơn nào).

Độ phân giải của các ID liên tục như vậy không được xác định bởi mô-đun pickle; nó sẽ ủy quyền độ phân giải này cho các phương thức do người dùng xác định trên bộ chọn và bộ giải nén, tương ứng là persistent_id()persistent_load().

Để chọn các đối tượng có ID liên tục bên ngoài, bộ chọn phải có phương thức persistent_id() tùy chỉnh lấy một đối tượng làm đối số và trả về None hoặc ID liên tục cho đối tượng đó. Khi None được trả về, bộ chọn chỉ cần chọn đối tượng như bình thường. Khi một chuỗi ID liên tục được trả về, trình chọn sẽ chọn đối tượng đó cùng với điểm đánh dấu để trình giải mã sẽ nhận ra đó là ID liên tục.

Để giải nén các đối tượng bên ngoài, trình giải nén phải có phương thức persistent_load() tùy chỉnh lấy đối tượng ID cố định và trả về đối tượng được tham chiếu.

Dưới đây là một ví dụ toàn diện trình bày cách sử dụng ID liên tục để chọn các đối tượng bên ngoài bằng cách tham chiếu.

 dụ # Simple trình bày cách sử dụng ID liên tục để chọn
đối tượng # external theo tham chiếu.

nhập khẩu dưa chua
nhập sqlite3
từ bộ sưu tập nhập têntuple

Lớp # Simple đại diện cho một bản ghi trong cơ sở dữ liệu của chúng tôi.
MemoRecord = nametuple("MemoRecord", "key, task")

lớp DBPickler(pickle.Pickler):

    def Persist_id(self, obj):
        # Instead chọn MemoRecord làm phiên bản lớp thông thường, chúng tôi phát ra một
        ID # persistent.
        nếu isinstance(obj, MemoRecord):
            # Here, ID cố định của chúng tôi chỉ đơn giản là một bộ dữ liệu, chứa một thẻ và một
            # key, đề cập đến một bản ghi cụ thể trong cơ sở dữ liệu.
            return ("MemoRecord", obj.key)
        khác:
            # If obj không có ID cố định, trả về Không có. Điều này có nghĩa là obj
            # needs được ngâm như bình thường.
            trả về Không 


lớp DBUnpickler(pickle.Unpickler):

    def __init__(bản thân, tập tin, kết nối):
        siêu().__init__(tệp)
        self.connection = kết nối

    def Persist_load(tự, pid):
        Phương thức # This được gọi bất cứ khi nào gặp ID liên tục.
        # Here, pid là bộ dữ liệu được DBPickler trả về.
        con trỏ = self.connection.cursor()
        loại_tag, key_id = pid
        nếu type_tag == "MemoRecord":
            # Fetch bản ghi được tham chiếu từ cơ sở dữ liệu và trả về nó.
            con trỏ.execute("SELECT * FROM bản ghi nhớ WHERE key=?", (str(key_id),))
            phím, tác vụ = con trỏ.fetchone()
            trả về MemoRecord(key, task)
        khác:
            # Always sẽ báo lỗi nếu bạn không thể trả về đúng đối tượng.
            # Otherwise, trình giải nén sẽ cho rằng Không có đối tượng nào được tham chiếu
            # by ID liên tục.
            nâng cao dưa.UnpicklingError("đối tượng liên tục không được hỗ trợ")


chắc chắn chính():
    nhập khẩu io
    nhập khẩu bản in

    # Initialize và điền vào cơ sở dữ liệu của chúng tôi.
    conn = sqlite3.connect(":memory:")
    con trỏ = conn.cursor()
    con trỏ.execute ("bản ghi nhớ CREATE TABLE (phím INTEGER PRIMARY KEY, nhiệm vụ TEXT)")
    nhiệm vụ = (
        'cho cá ăn',
        'chuẩn bị họp nhóm',
        'chiến đấu với ngựa vằn',
        )
    cho nhiệm vụ trong nhiệm vụ:
        con trỏ.execute("INSERT INTO bản ghi nhớ VALUES(NULL, ?)", (tác vụ,))

    # Fetch các bản ghi sẽ được ngâm.
    con trỏ.execute ("bản ghi nhớ SELECT * FROM")
    memos = [MemoRecord(key, task) cho key, task trong con trỏ]
    # Save các bản ghi bằng DBPickler tùy chỉnh của chúng tôi.
    tập tin = io.BytesIO()
    DBPickler(file).dump(bản ghi nhớ)

    print("Hồ sơ ngâm:")
    pprint.pprint(bản ghi nhớ)

    # Update một kỷ lục, chỉ để đánh giá thôi.
    con trỏ.execute("UPDATE ghi nhớ SET task='học tiếng Ý' WHERE key=1")

    # Load các bản ghi từ luồng dữ liệu dưa chua.
    tập tin.seek(0)
    memos = DBUnpickler(file, conn).load()

    print("Bản ghi chưa được chọn:")
    pprint.pprint(bản ghi nhớ)


nếu __name__ == '__main__':
    chính()

Bảng điều phối

Nếu một người muốn tùy chỉnh việc tẩy một số lớp mà không làm ảnh hưởng đến bất kỳ mã nào khác phụ thuộc vào việc tẩy, thì người ta có thể tạo một bộ chọn với một bảng điều phối riêng.

Bảng điều phối toàn cầu do mô-đun copyreg quản lý có sẵn dưới dạng copyreg.dispatch_table. Do đó, người ta có thể chọn sử dụng bản sao đã sửa đổi của copyreg.dispatch_table làm bảng điều phối riêng.

Ví dụ

f = io.BytesIO()
p = dưa chua.Pickler(f)
p.dispatch_table = copyreg.dispatch_table.copy()
p.dispatch_table[SomeClass] = less_SomeClass

tạo một phiên bản của pickle.Pickler với một bảng điều phối riêng xử lý lớp SomeClass một cách đặc biệt. Ngoài ra, mã

lớp MyPickler(pickle.Pickler):
    công văn_table = copyreg.dispatch_table.copy()
    Clark_table[SomeClass] = less_SomeClass
f = io.BytesIO()
p = MyPickler(f)

thực hiện tương tự nhưng tất cả các phiên bản của MyPickler theo mặc định sẽ chia sẻ bảng điều phối riêng. Mặt khác, mã

copyreg.pickle(SomeClass, less_SomeClass)
f = io.BytesIO()
p = dưa chua.Pickler(f)

sửa đổi bảng điều phối chung được chia sẻ bởi tất cả người dùng mô-đun copyreg.

Xử lý các đối tượng có trạng thái

Đây là một ví dụ cho thấy cách sửa đổi hành vi tẩy chua cho một lớp. Lớp TextReader bên dưới mở một tệp văn bản và trả về số dòng và nội dung dòng mỗi khi phương thức readline() của nó được gọi. Nếu một phiên bản TextReader được chọn, tất cả các thuộc tính except của thành viên đối tượng tệp sẽ được lưu. Khi phiên bản được bỏ chọn, tệp sẽ được mở lại và quá trình đọc sẽ tiếp tục từ vị trí cuối cùng. Các phương thức __setstate__()__getstate__() được sử dụng để thực hiện hành vi này.

lớp TextReader:
    """In và đánh số dòng trong một tệp văn bản."""

    def __init__(tự, tên tệp):
        self.filename = tên tệp
        self.file = open(tên tệp)
        tự.lineno = 0

    def readline(tự):
        self.lineno += 1
        dòng = self.file.readline()
        nếu không phải dòng:
            trả về Không 
        nếu line.endswith('\n'):
            dòng = dòng [:-1]
        return "%i: %s" % (self.lineno, line)

    chắc chắn __getstate__(tự):
        # Copy trạng thái của đối tượng từ self.__dict__ chứa
        # all thuộc tính phiên bản của chúng tôi. Luôn sử dụng dict.copy()
        # method để tránh sửa đổi trạng thái ban đầu.
        trạng thái = self.__dict__.copy()
        # Remove các mục không thể chọn được.
        del state['file']
        trạng thái trả về

    def __setstate__(tự, trạng thái):
        Thuộc tính phiên bản # Restore (tức là tên tệp và lineno).
        self.__dict__.update(state)
        # Restore trạng thái của tệp đã mở trước đó. Để làm như vậy, chúng ta cần phải
        # reopen và đọc từ đó cho đến khi số dòng được khôi phục.
        tập tin = mở (self.filename)
        cho _ trong phạm vi(self.lineno):
            tập tin.readline()
        # Finally, lưu tập tin.
        self.file = tập tin

Cách sử dụng mẫu có thể giống như thế này:

>>> reader = TextReader("hello.txt")
>>> reader.readline()
'1: Xin chào thế giới!'
>>> reader.readline()
'2: Tôi ở đường dây số hai.'
>>> new_reader = Pickle.loads(pickle.dumps(reader))
>>> new_reader.readline()
'3: Tạm biệt!'

Giảm tùy chỉnh cho các loại, chức năng và các đối tượng khác

Added in version 3.8.

Đôi khi, dispatch_table có thể không đủ linh hoạt. Cụ thể, chúng ta có thể muốn tùy chỉnh việc tẩy dựa trên một tiêu chí khác ngoài loại đối tượng hoặc chúng ta có thể muốn tùy chỉnh việc tẩy các hàm và lớp.

Đối với những trường hợp đó, có thể phân lớp từ lớp Pickler và triển khai phương thức reducer_override(). Phương thức này có thể trả về một bộ rút gọn tùy ý (xem __reduce__()). Ngoài ra, nó có thể trả về NotImplemented để dự phòng hành vi truyền thống.

Nếu cả dispatch_tablereducer_override() đều được xác định thì phương thức reducer_override() sẽ được ưu tiên.

Ghi chú

For performance reasons, reducer_override() may not be called for the following objects: None, True, False, and exact instances of int, float, bytes, str, dict, set, frozenset, list and tuple.

Here is a simple example where we allow pickling and reconstructing a given class:

nhập khẩu io
nhập khẩu dưa chua

lớp MyClass:
    thuộc tính của tôi = 1

lớp MyPickler(pickle.Pickler):
    def lesser_override(self, obj):
        """Bộ giảm tốc tùy chỉnh cho MyClass."""
        nếu getattr(obj, "__name__", Không ) == "MyClass":
            kiểu trả về, (obj.__name__, obj.__bases__,
                          {'my_attribute': obj.my_attribute})
        khác:
            # For bất kỳ đối tượng nào khác, dự phòng cho việc giảm thông thường
            trả về Chưa thực hiện

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

del MyClass

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

khẳng định isinstance(unpickled_class, type)
khẳng định unpickled_class.__name__ == "MyClass"
khẳng định unpickled_class.my_attribute == 1

Bộ đệm ngoài băng tần

Added in version 3.8.

Trong một số trường hợp, mô-đun pickle được sử dụng để truyền lượng dữ liệu khổng lồ. Do đó, điều quan trọng là giảm thiểu số lượng bản sao bộ nhớ để duy trì hiệu suất và mức tiêu thụ tài nguyên. However, normal operation of the pickle module, as it transforms a graph-like structure of objects into a sequential stream of bytes, intrinsically involves copying data to and from the pickle stream.

Ràng buộc này có thể được tránh nếu cả provider (triển khai các loại đối tượng được truyền) và consumer (triển khai hệ thống liên lạc) đều hỗ trợ các phương tiện truyền ngoài băng tần được cung cấp bởi giao thức dưa 5 trở lên.

Nhà cung cấp 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.

Đối tượng PickleBuffer signals mà bộ đệm cơ bản đủ điều kiện để truyền dữ liệu ngoài băng tần. Những đối tượng đó vẫn tương thích với việc sử dụng mô-đun pickle thông thường. Tuy nhiên, người tiêu dùng cũng có thể chọn tham gia để thông báo cho pickle rằng họ sẽ tự xử lý các bộ đệm đó.

Người tiêu dùng API

Hệ thống truyền thông có thể cho phép xử lý tùy chỉnh các đối tượng PickleBuffer được tạo khi tuần tự hóa biểu đồ đối tượng.

Về phía gửi, nó cần chuyển đối số buffer_callback cho Pickler (hoặc tới hàm dump() hoặc dumps()), đối số này sẽ được gọi với mỗi PickleBuffer được tạo trong khi chọn biểu đồ đối tượng. Các bộ đệm được buffer_callback tích lũy sẽ không thấy dữ liệu của chúng được sao chép vào luồng dưa chua, chỉ một điểm đánh dấu rẻ tiền sẽ được chèn vào.

Về phía bên nhận, nó cần chuyển đối số buffers cho Unpickler (hoặc tới hàm load() hoặc loads()), đây là một đối số có thể lặp lại của bộ đệm đã được chuyển tới buffer_callback. Việc lặp lại đó sẽ tạo ra các bộ đệm theo thứ tự giống như chúng được chuyển tới buffer_callback. Những bộ đệm đó sẽ cung cấp dữ liệu được mong đợi bởi các nhà xây dựng lại các đối tượng mà việc tẩy rửa đã tạo ra các đối tượng PickleBuffer ban đầu.

Giữa bên gửi và bên nhận, hệ thống thông tin liên lạc có thể tự do thực hiện cơ chế truyền riêng của mình đối với các bộ đệm ngoài băng tần. Các tối ưu hóa tiềm năng bao gồm việc sử dụng bộ nhớ dùng chung hoặc nén phụ thuộc vào kiểu dữ liệu.

Ví dụ

Đây là một ví dụ đơn giản trong đó chúng tôi triển khai một lớp con bytearray có thể tham gia vào quá trình lọc bộ đệm ngoài băng tần:

lớp ZeroCopyByteArray(bytearray):

    def __reduce_ex__(tự, giao thức):
        nếu giao thức >= 5:
            kiểu trả về(self)._reconstruct, (PickleBuffer(self),), Không 
        khác:
            # PickleBuffer bị cấm với các giao thức dưa chua <= 4.
            kiểu trả về(self)._reconstruct, (bytearray(self),)

    @classmethod
    def _reconstruct(cls, obj):
        với Memoryview(obj)  m:
            # Get xử lý đối tượng bộ đệm ban đầu
            obj = m.obj
            nếu loại (obj)  cls:
                Đối tượng bộ đệm # Original là ZeroCopyByteArray, hãy trả lại nó
                # as-is.
                trả lại đối tượng
            khác:
                trả về cls(obj)

Trình xây dựng lại (phương thức lớp _reconstruct) trả về đối tượng cung cấp bộ đệm nếu nó có đúng loại. Đây là một cách dễ dàng để mô phỏng hành vi không sao chép trên ví dụ đồ chơi này.

Về phía người tiêu dùng, chúng ta có thể chọn các đối tượng đó theo cách thông thường, khi hủy tuần tự hóa sẽ cung cấp cho chúng ta một bản sao của đối tượng ban đầu:

b = ZeroCopyByteArray(b"abc")
dữ liệu = dưa.dumps(b, giao thức=5)
new_b = dưa chua.loads(dữ liệu)
print(b == new_b) # True
print(b is new_b) # False: một bản sao đã được tạo

Nhưng nếu chúng tôi chuyển buffer_callback và sau đó trả lại bộ đệm tích lũy khi hủy tuần tự hóa, chúng tôi có thể lấy lại đối tượng ban đầu:

b = ZeroCopyByteArray(b"abc")
bộ đệm = []
dữ liệu = dưa.dumps(b, giao thức=5, buffer_callback=buffers.append)
new_b = Pickle.loads(dữ liệu, bộ đệm=bộ đệm)
print(b == new_b) # True
print(b is new_b) # True: không có bản sao nào được tạo ra

Ví dụ này bị hạn chế bởi thực tế là bytearray phân bổ bộ nhớ riêng của nó: bạn không thể tạo một phiên bản bytearray được hỗ trợ bởi bộ nhớ của đối tượng khác. Tuy nhiên, các kiểu dữ liệu của bên thứ ba như mảng NumPy không có giới hạn này và cho phép sử dụng phương pháp tẩy không bản sao (hoặc tạo càng ít bản sao càng tốt) khi chuyển giữa các quy trình hoặc hệ thống riêng biệt.

Xem thêm

PEP 574 -- Giao thức Pickle 5 với dữ liệu ngoài băng tần

Hạn chế toàn cầu

Theo mặc định, việc giải nén sẽ nhập bất kỳ lớp hoặc hàm nào mà nó tìm thấy trong dữ liệu dưa chua. Đối với nhiều ứng dụng, hành vi này không được chấp nhận vì nó cho phép trình giải mã nhập và gọi mã tùy ý. Chỉ cần xem xét luồng dữ liệu dưa chua thủ công này sẽ làm gì khi được tải

>>> nhập khẩu dưa chua
>>> Pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
xin chào thế giới
0

Trong ví dụ này, trình giải nén nhập hàm os.system() rồi áp dụng đối số chuỗi "echo hello world". Mặc dù ví dụ này không gây khó chịu nhưng không khó để tưởng tượng một ví dụ có thể làm hỏng hệ thống của bạn.

Vì lý do này, bạn có thể muốn kiểm soát những gì được bỏ chọn bằng cách tùy chỉnh Unpickler.find_class(). Không giống như tên gọi của nó, Unpickler.find_class() được gọi bất cứ khi nào một toàn cục (tức là một lớp hoặc một hàm) được yêu cầu. Vì vậy, có thể cấm hoàn toàn các tập hợp toàn cầu hoặc hạn chế chúng ở một tập hợp con an toàn.

Dưới đây là ví dụ về trình giải nén chỉ cho phép tải một số lớp an toàn từ mô-đun builtins

nhập nội dung
nhập khẩu io
nhập khẩu dưa chua

safe_buildins = {
    'phạm vi',
    'phức tạp',
    'đặt',
    'đông lạnh',
    'lát',
}

lớp Hạn chếUnpickler(pickle.Unpickler):

    def find_class(self, module, name):
        # Only cho phép các lớp an toàn từ nội trang.
        if -đun == "nội dung"  tên trong safe_buildins:
            trả về getattr(nội dung, tên)
        # Forbid mọi thứ khác.
        raisepick.UnpicklingError("toàn cầu '%s.%s' bị cấm" %
                                     (-đun, tên))

def bị hạn chế_loads:
    """Hàm trợ giúp tương tự như Pickle.loads()."""
    trả vềRestrictedUnpickler(io.BytesIO(s)).load()

Cách sử dụng mẫu của trình giải nén của chúng tôi hoạt động như dự định

>>> bị hạn chế_loads(pickle.dumps([1, 2, range(15)]))
[1, 2, phạm vi (0, 15)]
>>> bị hạn chế_loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
Traceback (cuộc gọi gần đây nhất):
  ...
dưa chua.UnpicklingError: 'os.system' toàn cầu bị cấm
>>> bị hạn chế_loads(b'cbuildins\neval\n'
... b'(S\'getattr(__import__("os"), "system")'
... b'("echo hello world")\'\ntR.')
Traceback (cuộc gọi gần đây nhất):
  ...
dưa chua.UnpicklingError: 'buildins.eval' toàn cầu bị cấm

Như các ví dụ của chúng tôi cho thấy, bạn phải cẩn thận với những gì bạn cho phép được giải quyết. Do đó, nếu vấn đề bảo mật là vấn đề đáng lo ngại, bạn có thể muốn xem xét các lựa chọn thay thế như API sắp xếp theo thứ tự trong xmlrpc.client hoặc các giải pháp của bên thứ ba.

Hiệu suất

Các phiên bản gần đây của giao thức dưa chua (từ giao thức 2 trở lên) có tính năng mã hóa nhị phân hiệu quả cho một số tính năng phổ biến và các loại tích hợp sẵn. Ngoài ra, mô-đun pickle có trình tối ưu hóa trong suốt được viết bằng C.

Ví dụ

Để có mã đơn giản nhất, hãy sử dụng hàm dump()load().

nhập khẩu dưa chua

# An arbitrary collection of objects supported by pickle.
dữ liệu = {
    'a': [1, 2.0, 3+4j],
    'b': ("chuỗi ký tự", b"chuỗi byte"),
    'c': {Không, Đúng, Sai}
}

với open('data.pickle', 'wb')  f:
    # Pickle từ điển 'dữ liệu' sử dụng giao thức cao nhất hiện có.
    Pickle.dump(data, f, Pickle.HIGHEST_PROTOCOL)

The following example reads the resulting pickled data.

nhập khẩu dưa chua

với open('data.pickle', 'rb')  f:
    Phiên bản giao thức # The được sử dụng sẽ được phát hiện tự động, vì vậy chúng tôi không
    # have để chỉ định nó.
    dữ liệu = dưa.load(f)

Giao diện dòng lệnh

Mô-đun pickle có thể được gọi dưới dạng tập lệnh từ dòng lệnh, nó sẽ hiển thị nội dung của tệp dưa chua. However, when the pickle file that you want to examine comes from an untrusted source, -m pickletools is a safer option because it does not execute pickle bytecode, see pickletools CLI usage.

python -m dưa chua_file [pickle_file ...]

Tùy chọn sau được chấp nhận:

pickle_file

Một tệp dưa để đọc hoặc - để biểu thị việc đọc từ đầu vào tiêu chuẩn.

Xem thêm

Mô-đun copyreg

Đăng ký trình xây dựng giao diện Pickle cho các loại tiện ích mở rộng.

Mô-đun pickletools

Công cụ để làm việc và phân tích dữ liệu đã chọn.

Mô-đun shelve

Cơ sở dữ liệu được lập chỉ mục của các đối tượng; sử dụng pickle.

Mô-đun copy

Sao chép đối tượng nông và sâu.

Mô-đun marshal

Tuần tự hóa hiệu suất cao của các loại tích hợp.

Chú thích cuối trang