11.1. pickle — Python 对象序列化

pickle模块实现了一个基本但Function强大的算法,用于对 Python 对象结构进行序列化和反序列化。 “ Pickling”是将 Python 对象层次结构转换为字节流的过程,而“ unpickling”是逆运算,从而字节流被转换回对象层次的过程。Pickling(和 Pickling)也被称为“序列化”,“编组”,“ [1]”或“拉平”,但是,为了避免混淆,此处使用的术语是“Pickling”和“Pickling”。

本文档介绍了pickle模块和cPickle模块。

Warning

pickle模块对于错误或恶意构建的数据并不安全。切勿挑剔从不可信或未经身份验证的来源收到的数据。

11.1.1. 与其他 Python 模块的关系

pickle模块具有一个优化的表弟,称为cPickle模块。顾名思义,cPickle是用 C 编写的,因此它可以比pickle快 1000 倍。但是,它不支持Pickler()Unpickler()类的子类,因为在cPickle中这些是函数,而不是类。大多数应用程序不需要此Function,并且可以受益于cPickle的改进性能。除此之外,两个模块的接口几乎完全相同。本手册介绍了通用接口,并在必要时指出了区别。在下面的讨论中,我们使用术语“ pickle”共同描述picklecPickle模块。

两个模块产生的数据流保证是可互换的。

Python 有一个更原始的序列化模块marshal,但总的来说pickle应该始终是序列化 Python 对象的首选方式。 marshal主要用于支持 Python 的.pyc文件。

pickle模块与marshal模块在几个重要方面不同:

  • pickle模块跟踪已序列化的对象,因此以后对同Pair象的引用将不会再次序列化。 marshal不这样做。

这对于递归对象和对象共享都有影响。递归对象是包含对其自身的引用的对象。这些不是由编组处理的,实际上,try编组递归对象将使您的 Python 解释器崩溃。当在序列化的对象层次结构中的不同位置存在对同Pair象的多个引用时,就会发生对象共享。 pickle仅将此类对象存储一次,并确保所有其他引用都指向主副本。共享的对象保持共享状态,这对于可变对象非常重要。

  • marshal不能用于序列化用户定义的类及其实例。 pickle可以透明地保存和恢复类实例,但是类定义必须是可导入的,并且与对象存储时位于同一模块中。

  • 不能保证marshal序列化格式可跨 Python 版本移植。因为它的主要工作就是支持.pyc文件,所以 Python 实现者保留在需要时以非向后兼容的方式更改序列化格式的权利。保证pickle序列化格式在所有 Python 版本中都向后兼容。

注意,序列化是比持久性更原始的概念。尽管pickle读写文件对象,但是它不能处理持久对象的命名问题,也不能处理(甚至更复杂的)并发访问持久对象的问题。 pickle模块可以将复杂的对象转换为字节流,并且可以将字节流转换为具有相同内部结构的对象。也许与这些字节流有关的最明显的事情是将它们写入文件中,但是也可以考虑pass网络发送它们或将它们存储在数据库中。模块shelve提供了一个简单的界面,可以对 DBM 样式的数据库文件中的对象进行腌制和解腌。

11.1.2. 数据流格式

pickle使用的数据格式特定于 Python。这样做的好处是不受外部标准(例如 XDR(不能表示指针共享))的限制;但是,这意味着非 Python 程序可能无法重建腌制的 Python 对象。

默认情况下,pickle数据格式使用可打印的 ASCII 表示形式。这比二进制表示要复杂得多。使用可打印 ASCII(以及pickle表示的其他一些 Feature)的最大优点是,出于调试或恢复目的,人们可以使用标准文本编辑器读取腌制的文件。

当前有 3 种不同的协议可用于 Pickling。

  • 协议版本 0 是原始 ASCII 协议,并且与 Python 的早期版本向后兼容。

  • 协议版本 1 是旧的二进制格式,也与 Python 的早期版本兼容。

  • 协议版本 2 是在 Python 2.3 中引入的。它提供了更有效的new-style class esPickling。

有关更多信息,请参考 PEP 307

如果未指定* protocol ,则使用协议 0.如果将 protocol *指定为负值或HIGHEST_PROTOCOL,则将使用可用的最高协议版本。

在版本 2.3 中进行了更改:引入了* protocol *参数。

pass指定* protocol *版本> = 1,可以选择效率更高的二进制格式。

11.1.3. Usage

要序列化对象层次结构,首先创建一个 Pickler,然后调用 Pickler 的dump()方法。要对数据流进行反序列化,请首先创建一个 unpickler,然后调用 unpickler 的load()方法。 pickle模块提供以下常量:

  • pickle. HIGHEST_PROTOCOL
    • 可用的最高协议版本。该值可以作为协议值传递。

2.3 版的新Function。

Note

确保始终以二进制模式打开使用协议> = 1 创建的 pickle 文件。对于旧的基于 ASCII 的 pickle 协议 0,只要保持一致,就可以使用文本模式或二进制模式。

在二进制模式下以协议 0 编写的 pickle 文件将包含单独的换行符作为换行符,因此,在记事本或其他不支持此格式的编辑器中查看时,它们看起来“很有趣”。

pickle模块提供以下Function,以使 Pickling 过程更加方便:

  • pickle. dump(* obj file * [,* protocol *])
    • 将* obj 的腌制表示形式写入打开的文件对象 file *。这等效于Pickler(file, protocol).dump(obj)

如果Ellipsis* protocol 参数,则使用协议 0.如果将 protocol *指定为负值或HIGHEST_PROTOCOL,则将使用最高协议版本。

在版本 2.3 中进行了更改:引入了* protocol *参数。

  • file *必须具有一个接受单个字符串参数的write()方法。因此,它可以是为写入而打开的文件对象,StringIO对象或满足此接口的任何其他自定义对象。
  • pickle. load(* file *)
    • 从打开的文件对象* file *中读取一个字符串,并将其解释为 pickle 数据流,从而重构并返回原始对象层次结构。这等效于Unpickler(file).load()
  • file 必须具有两个方法,一个带有整数参数的read()方法和一个不需要参数的readline()方法。这两个方法都应返回一个字符串。因此, file *可以是为读取而打开的文件对象,StringIO对象或符合此接口的任何其他自定义对象。

此Function自动确定数据流是否以二进制模式写入。

  • pickle. dumps(* obj * [,* protocol *])
    • 以字符串形式返回对象的腌制表示形式,而不是将其写入文件中。

如果Ellipsis* protocol 参数,则使用协议 0.如果将 protocol *指定为负值或HIGHEST_PROTOCOL,则将使用最高协议版本。

在版本 2.3 中进行了更改:添加了* protocol *参数。

  • pickle. loads(* string *)
    • 从字符串读取腌制的对象层次结构。超出腌制对象表示形式的字符串中的字符将被忽略。

pickle模块还定义了三个 exception:

  • exception pickle. PickleError

    • 下面定义的其他异常的通用 Base Class。这是从Exception继承的。
  • exception pickle. PicklingError

    • 当将不可拾取的对象传递给dump()方法时,将引发此异常。
  • exception pickle. UnpicklingError

pickle模块还导出两个可调用对象[2]PicklerUnpickler

    • class * pickle. Pickler(* file * [,* protocol *])
    • 这需要一个类似文件的对象,它将向其写入 pickle 数据流。

如果Ellipsis* protocol 参数,则使用协议 0.如果将 protocol *指定为负值或HIGHEST_PROTOCOL,则将使用最高协议版本。

在版本 2.3 中进行了更改:引入了* protocol *参数。

  • file *必须具有一个接受单个字符串参数的write()方法。因此,它可以是打开文件对象,StringIO对象,也可以是满足此接口的任何其他自定义对象。

Pickler个对象定义一个(或两个)公共方法:

  • dump(* obj *)

    • 将* obj 的腌制表示形式写入构造函数中提供的打开文件对象。根据传递给构造函数的 protocol *参数的值,将使用二进制或 ASCII 格式。
  • clear_memo ( )

    • 清除选取器的“备注”。备忘录是一种数据结构,可以记住腌制者已经看过的对象,以便共享或递归对象pass引用而不是pass值来腌制。重新使用腌制机时,此方法很有用。

Note

在 Python 2.3 之前,clear_memo()仅在由cPickle创建的选取器上可用。在pickle模块中,选取器有一个名为memo的实例变量,它是 Python 字典。因此,要清除pickle模块选取器的备注,可以执行以下操作:

mypickler.memo.clear()

不需要支持旧版本 Python 的代码应该只使用clear_memo()

可以多次调用同一Pickler实例的dump()方法。然后,它们必须与对相应Unpickler实例的load()方法的相同调用次数匹配。如果同Pair象被多个dump()调用腌制,则load()将全部产生对同Pair象的引用。 [3]

Unpickler个对象定义为:

  • 类别 pickle. Unpickler(文件)
    • 这需要一个类似文件的对象,它将从其中读取一个 pickle 数据流。此类自动确定是否以二进制模式写入数据流,因此不需要像Pickler工厂中那样的标志。
  • file 必须具有两个方法,一个带有整数参数的read()方法和一个不需要参数的readline()方法。这两个方法都应返回一个字符串。因此, file *可以是为读取而打开的文件对象,StringIO对象或符合此接口的任何其他自定义对象。

Unpickler个对象具有一个(或两个)公共方法:

  • load ( )
    • 从构造函数中提供的打开文件对象中读取一个腌制的对象表示形式,并返回其中指定的重构对象层次结构。

此方法自动确定数据流是否以二进制模式写入。

  • noload ( )
    • 就像load()一样,除了它实际上不创建任何对象。这主要用于查找可在 pickle 数据流中引用的所谓的“持久性 ID”。有关更多详细信息,请参见下面的pickle 协议部分。

注意: noload()方法当前仅在使用cPickle模块创建的Unpickler对象上可用。 pickle模块Unpickler没有noload()方法。

11.1.4. 什么可以腌制和不腌制?

可以腌制以下类型:

  • NoneTrueFalse

  • 整数,长整数,浮点数,复数

  • 普通和 Unicode 字符串

  • 仅包含可腌制对象的 Tuples,列表,集合和词典

  • 在模块顶层定义的Function

  • 在模块顶层定义的内置函数

  • 在模块顶层定义的类

  • 此类的实例,它们的dict或调用getstate()的结果是可腌制的(有关详细信息,请参见pickle 协议部分)。

try腌制不可腌制的物体将引发PicklingError异常;发生这种情况时,未指定的字节数可能已被写入基础文件。try腌制高度递归的数据结构可能会超出最大递归深度,在这种情况下将引发RuntimeError。您可以使用sys.setrecursionlimit()小心地提高此限制。

请注意,函数(内置的和用户定义的)是pass“完全限定”的名称引用而不是值进行 Pickling 的。这意味着仅对函数名称以及在其中定义该函数的模块的名称进行腌制。既不腌制函数的代码,也不腌制其任何函数属性。因此,定义模块必须在解酸环境中是可导入的,并且该模块必须包含命名对象,否则将引发异常。 [4]

同样,pass命名引用对类进行 Pickling,因此在未 Pickling 环境中也适用相同的限制。请注意,没有对类的代码或数据进行 Pickling,因此在下面的示例中,在未 Pickling 的环境中未还原类属性attr

class Foo:
    attr = 'a class attr'

picklestring = pickle.dumps(Foo)

这些限制是为什么必须在模块的顶层定义可腌制函数和类的原因。

同样,当对类实例进行腌制时,其类的代码和数据也不会与其一起被腌制。仅实例数据被腌制。这样做是有意为之的,因此您可以修复类中的错误或向类中添加方法,并仍然加载使用该类的早期版本创建的对象。如果计划使用寿命很长的对象,而该对象将看到类的许多版本,则可能值得在对象中放入版本号,以便可以pass类的setstate()方法进行适当的转换。

11.1.5. pickle 协议

本节描述了“Pickling 协议”,该协议定义了 Pickling 程序/取消 Pickling 程序和要序列化的对象之间的接口。该协议为您提供了一种定义,定制和控制对象序列化和反序列化方式的标准方法。本节中的描述未涵盖可用于使不 Pickling 数据流中的不 Pickling 环境更安全的特定自定义项;有关详情,请参见第Subclassing Unpicklers节。

11.1.5.1. 腌制和解开普通类实例

  • object. __getinitargs__ ( )

    • 取消腌制的类实例时,通常调用其init()方法。如果希望在解开时调用init()方法,则老式类可以定义方法getinitargs(),该方法应返回* tuple *位置参数以传递给类构造函数(例如init())。不支持关键字参数。腌制时调用getinitargs()方法;它返回的 Tuples 并入实例的 pickle 中。
  • object. __getnewargs__ ( )

    • new-style类型可以提供用于协议 2 的getnewargs()方法。如果在创建实例时该类型构建了一些内部不变量,或者如果内存分配受传递给new()方法的值影响,则需要实现此方法。类型(用于 Tuples 和字符串)。 new-style class C的实例使用
obj = C.__new__(C, *args)

其中* args *是在原始对象上调用getnewargs()的结果;如果没有getnewargs(),则假定为空的 Tuples。

  • object. __getstate__ ( )

    • 类可以进一步影响其实例的腌制方式。如果该类定义了方法getstate(),则会调用该方法并将返回状态作为实例的内容进行腌制,而不是实例的字典的内容。如果没有getstate()方法,则对实例的dict进行腌制。
  • object. __setstate__(* state *)

    • 取消拾取后,如果该类还定义了方法setstate(),则会以未拾取状态调用它。 [5]如果没有setstate()方法,则腌制状态必须是字典,并且其项已分配给新实例的字典。如果一个类同时定义了getstate()setstate(),则状态对象不必是字典,并且这些方法可以完成所需的工作。 [6]

Note

对于new-style class es,如果getstate()返回错误值,则不会调用setstate()方法。

Note

在解开时间,可能会在实例上调用诸如getattr()getattribute()setattr()之类的方法。如果这些方法依赖于某个内部不变式为真,则该类型应实现getinitargs()getnewargs()以构建这样的不变式;否则,将不会调用new()init()

11.1.5.2. Pickling 和解酸扩展类型

  • object. __reduce__ ( )
    • Pickler遇到某个类型的对象时,它不知道任何内容(例如扩展类型),它会在两个地方寻找如何腌制它的提示。一种替代方法是使对象实现reduce()方法。如果提供,则在 Pickling 时间reduce()将不带任何参数被调用,并且它必须返回字符串或 Tuples。

如果返回一个字符串,它将命名一个全局变量,其内容将被正常腌制。 reduce()返回的字符串应该是对象相对于其模块的本地名称; pickle 模块搜索模块名称空间以确定对象的模块。

返回一个 Tuples 时,它的长度必须在 2 到 5 个元素之间。可选元素可以Ellipsis,也可以提供None作为其值。将该 Tuples 的内容照常进行腌制,并在去腌制时用于重建对象。每个元素的语义是:

  • 将被调用以创建对象的初始版本的可调用对象。Tuples 的下一个元素将为此可调用对象提供参数,而后一个元素将提供其他状态信息,这些信息随后将用于完全重建腌制的数据。

在解开环境中,此对象必须是一个类,注册为“安全构造函数”的可调用对象(请参见下文),或者它必须具有带有真实值的属性__safe_for_unpickling__。否则,将在解酸环境中引发UnpicklingError。注意,像往常一样,可调用对象本身是按名称腌制的。

  • 可调用对象的参数 Tuples。

在版本 2.5 中更改:以前,此参数也可以是None

  • (可选)对象的状态,该状态将传递给对象的setstate()方法,如腌制和解开普通类实例部分所述。如果对象没有setstate()方法,则如上所述,该值必须是字典,并将其添加到对象的dict中。

  • (可选)迭代器(而不是序列)产生连续的列表项。这些列表项将被腌制,并使用obj.append(item)obj.extend(list_of_items)附加到对象。这主要用于列表子类,但其他类也可以使用,只要它们具有带有适当签名的append()extend()方法。 (是否使用append()extend()取决于使用哪种 pickle 协议版本以及要附加的项目数,因此必须同时支持.)

  • 可选地,一个迭代器(不是序列)产生连续的字典项,该字典项应为(key, value)形式的 Tuples。这些项目将被腌制并使用obj[key] = value存储到对象。这主要用于字典子类,但其他类可以实现setitem(),但可以使用它们。

  • object. __reduce_ex__(* protocol *)

object类同时实现reduce()reduce_ex();但是,如果子类覆盖reduce()而不是reduce_ex(),则reduce_ex()实现会检测到此情况并调用reduce()

在要腌制的对象上实现reduce()方法的另一种方法是在copy_reg模块中注册可调用对象。该模块为程序提供了一种注册“归约函数”和用户定义类型的构造函数的方法。归约函数具有与上述reduce()方法相同的语义和接口,不同之处在于它们使用单个参数(要腌制的对象)调用。

如上所述,出于析出的目的,已注册的构造函数被视为“安全构造函数”。

11.1.5.3. Pickling 和去 Pickling 外部物体

为了实现对象持久性,pickle模块支持对腌制数据流外部的对象进行引用的概念。此类对象由“永久 ID”引用,该 ID 只是可打印 ASCII 字符的任意字符串。 pickle模块未定义此类名称的解析;它将把此解决方案委派给 Pickler 和 UnPickler 上用户定义的Function。 [7]

要定义外部持久 ID 解析,您需要设置 pickler 对象的persistent_id属性和 unpickler 对象的persistent_load属性。

要使具有外部持久性 ID 的对象腌制,selectors 必须具有一个自定义persistent_id()方法,该方法将对象作为参数并返回None或该对象的持久性 ID。当返回None时,selectors 将简单地照常对对象进行 Pickling。返回持久性 ID 字符串后,腌制人员将腌制该字符串以及标记,以便取消腌制人员将字符串识别为持久性 ID。

要解除对外部对象的限制,解除请求程序必须具有一个自定义persistent_load()函数,该函数接受一个永久 ID 字符串并返回引用的对象。

这是一个愚蠢的例子,可能揭示了更多的信息:

import pickle
from cStringIO import StringIO

src = StringIO()
p = pickle.Pickler(src)

def persistent_id(obj):
    if hasattr(obj, 'x'):
        return 'the value %d' % obj.x
    else:
        return None

p.persistent_id = persistent_id

class Integer:
    def __init__(self, x):
        self.x = x
    def __str__(self):
        return 'My name is integer %d' % self.x

i = Integer(7)
print i
p.dump(i)

datastream = src.getvalue()
print repr(datastream)
dst = StringIO(datastream)

up = pickle.Unpickler(dst)

class FancyInteger(Integer):
    def __str__(self):
        return 'I am the integer %d' % self.x

def persistent_load(persid):
    if persid.startswith('the value '):
        value = int(persid.split()[2])
        return FancyInteger(value)
    else:
        raise pickle.UnpicklingError, 'Invalid persistent id'

up.persistent_load = persistent_load

j = up.load()
print j

cPickle模块中,也可以将取消选取程序的persistent_load属性设置为 Python 列表,在这种情况下,当取消选取程序达到持久 ID 时,持久 ID 字符串将简单地附加到此列表中。存在此Function的目的是,可以“嗅探”pickle 数据流以进行对象引用,而无需实际实例化 pickle 中的所有对象。 [8]persistent_load设置为列表通常与 Unpickler 上的noload()方法结合使用。

11.1.6. 子类化 Unpicklers

默认情况下,取消腌制将导入它在腌制数据中找到的任何类。您可以pass自定义取消选取器来精确控制哪些内容未被选取以及哪些内容被调用。不幸的是,具体取决于您使用的是pickle还是cPickle,您的操作方式有所不同。 [9]

pickle模块中,您需要从Unpickler派生一个子类,从而覆盖load_global()方法。 load_global()应该从 pickle 数据流中读取两行,其中第一行将是包含该类的模块的名称,第二行将是该实例的类的名称。然后,它查找该类,可能会导入模块并挖掘属性,然后将找到的内容附加到取消拾取程序的堆栈中。稍后,该类将被分配给空类的__class__属性,这是一种神奇地创建实例而无需调用其类init()的方式。您的工作(您应该选择接受)是load_global()推入卸货员的烟囱,这是您认为可以安全卸料的任何类的已知安全版本。产生这样的类取决于您。或者,如果您想禁止实例的全部取消操作,则可能会引发错误。如果这听起来像是骇客,那是对的。请参考源代码以使此工作。

使用cPickle可以使事情变得更干净,但幅度不大。要控制哪些东西不被 Pickling,您可以将不 Pickling 的find_global属性设置为一个函数或None。如果为None,则try解除实例修复的任何try都会引发UnpicklingError。如果它是一个函数,则它应该接受一个模块名和一个类名,并返回相应的类对象。它负责查找类并执行任何必要的导入,并且可能引发错误,以防止类的实例被解开。

这个故事的寓意是,您应该对应用程序释放的字符串的来源非常小心。

11.1.7. Example

对于最简单的代码,请使用dump()load()函数。请注意,将自动腌制并正确还原自引用列表。

import pickle

data1 = {'a': [1, 2.0, 3, 4+6j],
         'b': ('string', u'Unicode string'),
         'c': None}

selfref_list = [1, 2, 3]
selfref_list.append(selfref_list)

output = open('data.pkl', 'wb')

# Pickle dictionary using protocol 0.
pickle.dump(data1, output)

# Pickle the list using the highest protocol available.
pickle.dump(selfref_list, output, -1)

output.close()

以下示例读取所得的腌制数据。读取包含 pickle 的文件时,应以二进制模式打开文件,因为无法确定是否使用 ASCII 或二进制格式。

import pprint, pickle

pkl_file = open('data.pkl', 'rb')

data1 = pickle.load(pkl_file)
pprint.pprint(data1)

data2 = pickle.load(pkl_file)
pprint.pprint(data2)

pkl_file.close()

这是一个更大的示例,显示了如何修改类的 Pickling 行为。 TextReader类打开一个文本文件,并在每次调用readline()方法时返回行号和行内容。如果腌制了一个TextReader实例,则保存文件对象成员的所有属性(除外)。取消实例化后,将重新打开文件,并从最后一个位置恢复读取。 setstate()getstate()方法用于实现此行为。

#!/usr/local/bin/python

class TextReader:
    """Print and number lines in a text file."""
    def __init__(self, file):
        self.file = file
        self.fh = open(file)
        self.lineno = 0

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

    def __getstate__(self):
        odict = self.__dict__.copy() # copy the dict since we change it
        del odict['fh']              # remove filehandle entry
        return odict

    def __setstate__(self, dict):
        fh = open(dict['file'])      # reopen file
        count = dict['lineno']       # read from file...
        while count:                 # until line count is restored
            fh.readline()
            count = count - 1
        self.__dict__.update(dict)   # update attributes
        self.fh = fh                 # save the file object

用法示例可能是这样的:

>>> import TextReader
>>> obj = TextReader.TextReader("TextReader.py")
>>> obj.readline()
'1: #!/usr/local/bin/python'
>>> obj.readline()
'2: '
>>> obj.readline()
'3: class TextReader:'
>>> import pickle
>>> pickle.dump(obj, open('save.p', 'wb'))

如果您希望pickle跨 Python 进程运行,请在 continue 之前启动另一个 Python 会话。相同的过程或新的过程都可能发生以下情况。

>>> import pickle
>>> reader = pickle.load(open('save.p', 'rb'))
>>> reader.readline()
'4:     """Print and number lines in a text file."""'

See also

  • Module copy_reg

  • 扩展类型的 Pickle 接口构造函数注册。

  • Module shelve

  • 索引的对象数据库;使用pickle

  • Module copy

  • 浅而深的对象复制。

  • Module marshal

  • 内置类型的高性能序列化。

11.2. cPickle —更快的 pickle

cPickle模块支持 Python 对象的序列化和反序列化,提供的接口和Function与pickle模块几乎相同。有几个差异,最重要的是性能和可分类性。

首先,cPickle的速度比pickle快 1000 倍,因为前者是用 C 语言实现的。其次,在cPickle模块中,可调用的Pickler()Unpickler()是函数,而不是类。这意味着您不能使用它们来派生自定义的 Pickling 和不 Pickling 子类。大多数应用程序不需要此Function,并且应该受益于cPickle模块大大提高的性能。

picklecPickle产生的 pickle 数据流是相同的,因此可以将picklecPickle与现有的 pickle 互换使用。 [10]

cPicklepickle之间在 API 上还有其他细微差别,但是对于大多数应用程序来说,它们是可以互换的。 pickle模块文档中提供了更多文档,其中包括已记录差异的列表。

Footnotes

  • [1]

    • 不要将此与marshal模块混淆
  • [2]

    • pickle模块中,这些可调用对象是类,您可以将其子类化以自定义行为。但是,在cPickle模块中,这些可调用对象是工厂函数,因此不能被子类化。子类化的一个常见原因是控制哪些对象实际上可以被去除。有关更多详细信息,请参见第Subclassing Unpicklers节。
  • [3]

    • 警告:用于腌制多个对象,而无需干预对象或其 Component 的修改。如果您修改了一个对象,然后使用相同的Pickler实例再次对其进行了腌制,则该对象不会再次被腌制-对它的引用将被腌制,并且Unpickler将返回旧值,而不是修改后的值。这里有两个问题:(1)检测更改,(2)整理一组最小的更改。垃圾收集在这里也可能成为问题。
  • [4]

  • [5]

    • 这些方法也可以用于实现复制类实例。
  • [6]

    • copy模块中定义的浅层和深层复制操作也使用此协议。
  • [7]

    • 关联这些用户定义Function的实际机制对于picklecPickle略有不同。此处给出的描述对于两种实现方式均起作用。 pickle模块的用户还可以使用子类来实现相同的结果,从而覆盖派生类中的persistent_id()persistent_load()方法。
  • [8]

    • 我们将为您留下 Guido 和 Jim 的图像,他们坐在他们的客厅里闻着咸菜。
  • [9]

    • 请注意:此处描述的机制使用内部属性和方法,这些属性和方法在将来的 Python 版本中可能会发生变化。我们打算有一天提供一个用于控制此行为的通用接口,该接口可在picklecPickle中工作。
  • [10]

    • 由于 pickle 数据格式实际上是一种很小的面向堆栈的编程语言,并且某些对象的编码具有一定的自由度,因此两个模块可能为同一 Importing 对象生成不同的数据流。但是,可以保证它们始终能够读取彼此的数据流。