Python标准库中的pickle模块



pickle  -  Python对象序列化。

        pickle模块实现了用于序列化和反序列化Python对象结构的二进制协议。“pickle”是将Python对象层次结构转换为一个字节流的过程,而“unpickle”是反向操作,即一个字节流(从二进制文件或字节类对象)转换回对象层次结构。Pickling(和unpickling)也称为“序列化”、“编组”或“扁平化”;然而,为了避免混淆,这里使用的术语是“pickling”和“unpickling”。

        警告:pickle模块不能防止错误或恶意构造的数据。从不从不可信或未经身份验证的源接收数据。

pickle模块不同于marshal的几个重要方式:

        pickle模块跟踪它已经序列化的对象,以便以后对同一对象的引用不会再次被序列化。marshal不是这样做的。

        这对递归对象和对象共享都有影响。递归对象是包含对自己的引用的对象。这些都是marshal不能处理的,事实上,marshal试图序列化递归对象会使Python解释器崩溃;对象共享发生在对象层次结构中不同位置的同一对象有多个引用时。pickle只存储此类对象一次,并确保所有其他引用指向主副本。共享对象仍然共享,这对于可变对象非常重要。

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

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

pickle与json比较

        pickle协议和JSON  (JavaScript对象表示法)之间有基本的区别:
        JSON是一种文本序列化格式(它输出unicode文本,尽管大多数时候它被编码为utf-8),而pickle是二进制序列化格式;
        JSON是人类可读的,而pickle则不是;
        JSON在Python生态系统之外是可互操作和广泛使用的,而pickle是特定于Python的;
        默认情况下,JSON只能表示Python内置类型的一个子集,并且没有自定义类;pickle可以表示极其大量的Python类型(其中许多是自动的,通过聪明地使用Python的内省设施,复杂的情况可以通过实现特定的对象api来解决)。

数据流格式

        pickle所使用的数据格式是python特有的。这有一个优点,即没有像JSON或XDR这样的外部标准强加的限制(它们不能代表指针共享);然而,这意味着非Python程序可能无法重构pickle的Python对象。

        默认情况下,pickle数据格式使用相对紧凑的二进制表示形式。如果您需要最优的尺寸特性,您可以有效地压缩pickle数据。

        模块pickletools包含用于分析pickle生成的数据流的工具。pickletools源代码对pickle协议使用的操作码有广泛的评论。

        目前有5种不同的协议可以用于pickle。越新的Python版本,使用的pickle协议越高:
        协议版本0是最初的“人类可读”协议,并且向后兼容Python的早期版本。
        协议版本1是一种旧的二进制格式,它也与早期版本的Python兼容。
        在Python  2.3中引入了协议版本2。它提供了更有效的新式类的pickle。参考PEP  307,了解协议2所带来的改进信息。
        在Python  3.0中添加了协议版本3。它对字节对象有明确的支持,并且不能被Python  2.x  unpickle。这是默认的协议,在需要与其他Python  3版本兼容时推荐的协议。
        在Python  3.4中添加了协议版本4。它增加了对非常大的对象的支持,对更多的对象进行pickle,以及一些数据格式的优化。参考PEP  3154,了解第4号协议带来的改进信息。

模块接口

        要序列化对象层次结构,只需调用dump()函数。类似地,要反序列化数据流,可以调用load()函数。但是,如果您希望对序列化和反序列化有更多的控制,则可以分别创建一个Pickler或Unpickler对象。

pickle模块提供以下常量:
        pickle.HIGHEST_PROTOCOL
        一个整数,最高的协议版本可用。这个值可以作为一个协议值传递给函数dump()和dumps()  以及Pickler构造函数。

        pickle.DEFAULT_PROTOCOL
        一个整数,用于pickle的默认协议版本。可能小于HIGHEST_PROTOCOL。目前默认的协议是3,这是为python3设计的新协议。

        pickle模块提供了以下函数,使序列化相关过程更加方便:

        pickle.dump(obj,  file,  protocol=None,  *,  fix_imports=True)
        将pickle表示的obj写入打开的文件对象文件中。这相当于Pickler(file,  protocol).dump(obj)。
        参数:
        protocol-一个整数,告诉pickler使用给定的协议;支持的协议是0到HIGHEST_PROTOCOL。如果没有指定,默认是DEFAULT_PROTOCOL。如果指定了负数,则选择HIGHEST_PROTOCOL。
        file-必须有一个write()方法,该方法接受单个字节的参数。因此,它可以是一个用于二进制写入的磁盘上的文件、或一个io.BytesIO实例、或任何其他符合该接口的自定义对象。
        fix_import-如果是true并且协议小于3,pickle将尝试将新的Python  3名称映射到Python  2中使用的旧模块名,这样pickle数据流就可以通过python2来读取。

        pickle.dumps(obj,  protocol=None,  *,  fix_imports=True)
        将对象的pickle表示作为一个字节对象返回,而不是将其写入文件。
        参数协议和fix_import与dump()的相同。

        pickle.load(file,  *,  fix_imports=True,  encoding="ASCII",  errors="strict")
        从打开的文件对象文件中读取pickle的对象表示,并返回其中指定的重构对象层次结构。这相当于  Unpickler(file).load()。
        该pickle的协议版本是自动检测的,因此不需要任何协议参数。字节经过pickle对象的表示被忽略。
        参数file必须有两个方法,一个是读取整数参数的read()方法,另一个是readline()方法,不需要参数。这两种方法都应该返回字节。因此,文件可以是一个打开的磁盘文件,用于二进制读取、或一个io.BytesIO对象、或任何其他符合该接口的自定义对象。
        可选的关键字参数是fix_import、encoding和errors,它们用于控制由Python  2生成的pickle流的兼容性支持。如果fix_import是True,pickle将尝试将旧的Python  2名称映射到Python  3中使用的新名称。encoding和errors告诉pickle如何解码由Python  2  pickle的8位字符串实例;这些默认值分别为“ASCII”和“strict”。编码可以是“bytes”来读取这些8位字符串实例作为字节对象。

        pickle.loads(bytes_object,  *,  fix_imports=True,  encoding="ASCII",  errors="strict")
        从一个字节对象中读取一个pickle的对象层次结构,并返回其中指定的重构对象层次结构。
        参数请参考pickle.load。

        pickle模块定义了三个异常:
        异常pickle.PickleError
        其他pickle异常的公共基类。它继承了异常。
        异常pickle.PicklingError
        在Pickler中遇到不可pickle的对象时引发的错误。它继承了PickleError。
        异常pickle.UnpicklingError
        当出现问题时,会引发错误,例如数据损坏或安全违规。它继承了PickleError。

        注意,在unpickle期间可能还会出现其他异常,包括(但不一定限于)AttributeError、EOFError、ImportError和IndexError。
 

pickle模块包含两个类,Pickler和Unpickler:

        class  pickle.Pickler(file,  protocol=None,  *,  fix_imports=True)
        这需要一个二进制文件来编写pickle数据流。
        参数参考pickle.dump。

        dump(obj)
        将pickle表示的obj写入构造函数中给定的打开文件对象。

        persistent_id(obj)  #持久ID
        默认不做任何事。它存在是为了子类可以覆盖它。
        如果persistent_id()返回None,那么obj就像往常一样被pickle。任何其他值都会导致Pickler将返回的值释放为obj的持久ID。这个持久ID的含义应该由Unpickler.persistent_load()定义。请注意,persistent_id()返回的值本身不能具有持久ID。
        有关详细信息和使用示例参见外部对象的持久化。

        dispatch_table  #分派表  3.3版本中新增
        pickler对象的分派表是一种可以使用copyreg.pickle()声明的简化函数的注册表。它是一个映射,其键是类,其值是还原函数。reduce函数接受关联类的单个参数,并应遵循与__reduce__()方法相同的接口。
        默认情况下,pickler对象将没有dispatch_table属性,它将使用copyreg模块管理的全局调度表。但是,要为特定的pickler对象定制pickle,可以将dispatch_table属性设置为类似于dict的对象。或者,如果Pickler的子类具有dispatch_table属性,那么它将作为该类的实例的默认调度表。
        请参阅调度表以获得使用示例。

        fast
        已弃用。如果设置为真值,启用快速模式。快速模式禁用了备忘录的使用,因此加快了序列化过程,而不是产生多余的PUT操作码。它不应该与自引用对象一起使用,否则将会导致Pickler无限地递归。
        如果你需要更紧凑的序列化,可以使用pickletools.optimize()。

        class  pickle.Unpickler(file,  *,  fix_imports=True,  encoding="ASCII",  errors="strict")
        从一个二进制文件来读取pickle数据流。
        参数参考pickle.load

        load()
        从构造函数中给出的打开文件对象中读取pickle对象表示,并返回其中指定的重构对象层次结构。字节经过pickle对象的表示被忽略。

        persistent_load(pid)
        在默认情况下增加一个UnpicklingError。
        如果定义了,persistent_load()应该返回持久ID  pid指定的对象。如果遇到一个无效的持久ID,就应该抛出一个UnpicklingError。
        有关详细信息和使用示例的外部对象的持久化。

        find_class(module,  name)
        如果需要,导入模块,并返回名为name的对象,其中模块和名称参数是str对象。注意,与它同名函数find_class()也用于查找函数。

        子类重写以获得对对象类型的控制、以及如何加载它们从而可能降低安全风险。详细信息请参阅限制全局变量。

下列类型可以序列化:
None,  True,  and  False
integers,  floating  point  numbers,  complex  numbers
strings,  bytes,  bytearrays
tuples,  lists,  sets,  and  dictionaries  containing  only  picklable  objects
functions  defined  at  the  top  level  of  a  module  (using  def,  not  lambda)
#在模块的顶层定义的函数(使用def,而不是lambda)
built-in  functions  defined  at  the  top  level  of  a  module
#内置函数定义在模块的顶层。
classes  that  are  defined  at  the  top  level  of  a  module
#在模块的顶层定义的类。
instances  of  such  classes  whose  __dict__  or  the  result  of  calling  __getstate__()  is  picklable  (see  section  Pickling  Class  Instances  for  details).
#此类类的实例,其__dict__或调用__getstate__()的结果是可选的(详细信息请参见pickle类实例)。

        pickle  unpicklable对象的尝试有可能引发PicklingError异常。当发生这种情况时,可能已经向底层文件写入了一些字节。尝试pickle一个高度递归的数据结构可能会超过最大递归深度,在这种情况下会出现一个递归错误。您可以使用sys.setrecursionlimit()小心地提高这个限制。

        注意,函数(内置的和用户定义的)是通过“完全限定”的名称引用而不是由值来pickle的。这意味着只有函数名被pickle。函数被定义的模块的名称、函数的代码和函数的任何属性都没有被pickle。因此,在unpickle环境中,定义模块必须是可输入的,并且模块必须包含指定的对象,否则将会引发异常。

        类似地,类通过命名引用进行pickle,因此在unpickle环境中也适用相同的限制。注意,所有类的代码或数据都没有被pickle,因此在以下示例中,在unpickle环境中没有恢复class属性attr:

class  Foo:
        attr  =  'A  class  attribute'

picklestring  =  pickle.dumps(Foo)

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

        类似地,当pickle类实例时,它们的类的代码和数据不会与它们一起pickle。只有实例数据被pickle。这样做是有目的的,这样您就可以在类中修复bug,或者向类添加方法,并且仍然可以使用类的早期版本创建的对象。如果您计划使用长时间的对象,将会看到一个类的多个版本,那么在对象中放置一个版本号可能是值得的,这样可以通过类的__setstate__()方法来实现适当的转换。




Pickling  Class实例

        在本节中,我们将描述可用于定义、自定义和控制类实例如何被pickle和未pickle的通用机制。

        在大多数情况下,不需要额外的代码来让实例变得可pickle。默认情况下,pickle将通过内省检索类和实例的属性。当一个类实例被unpickle时,它的__init__()方法通常不会被调用。默认行为首先创建一个未初始化的实例,然后恢复保存的属性。下面的代码显示了这种行为的实现:


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

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



        类可以通过提供一种或几种特殊方法来改变默认行为:
        object.__getnewargs_ex__()
        在协议2和更新中,实现__getnewargs_ex__()方法的类可以指定在unpickle时传递给__new__()方法的值。该方法必须返回一对(args,  kwargs),其中args是位置参数的元组,kwargs是一个用于构造对象的命名参数的字典。这些将被传递到unpickle的__new__()方法。
如果类的__new__()方法需要关键字参数,则应该实现此方法。否则,建议兼容实现__getnewargs__()。
        在版本3.6中更改:__getnewargs_ex__()现在在协议2和3中使用。

        object.__getnewargs__()
        此方法与__getnewargs_ex__()类似,但只支持位置参数。它必须返回一组参数args,这些参数将被传递给unpickle的__new__()方法。
        如果定义__getnewargs_ex__(),则不会调用__getnewargs__()。
        版本3.6:在Python  3.6之前,__getnewargs__()在协议2和3中被调用而不是__getnewargs_ex__()。

        object.__getstate__()
        类可以进一步影响其实例的pickle方式;如果类定义了__getstate__()方法,则调用它,将返回的对象作为实例的内容进行pickle,而不是实例字典的内容。如果缺少__getstate__()方法,那么实例的__dict__将像往常一样被pickle。

        object.__setstate__(state)
        在unpickle中,如果类定义__setstate__(),则调用unpickle状态。在这种情况下,状态对象不需要是字典。否则,pickle状态必须是一个字典,它的条目被分配给新实例的字典。

        object.__reduce__()
        接口目前定义如下。__reduce__()方法不接受任何参数,并返回一个字符串,或者最好是一个tuple(返回的对象通常被称为“reduce值”)。

        如果返回一个字符串,则该字符串应该被解释为全局变量的名称。它应该是对象的本地名称相对于它的模块;pickle模块搜索模块名称空间以确定对象的模块。这种行为通常适用于单身者。
当一个元组被返回时,它必须在2到5个项目之间。可选项可以省略,也可以不作为其值提供。每一项的语义学顺序如下:
        一个可调用的对象,它将被调用来创建对象的初始版本。
        可调用对象的一组参数。如果调用方不接受任何参数,则必须给出空元组。
        可选地,对象的状态,将被传递到对象的__setstate__()方法,如前所述。如果对象没有这样的方法,那么该值必须是一个字典,它将被添加到对象的__dict__属性中。
        可选地,一个迭代器(而不是一个序列)产生连续的项。这些项将被追加到对象中,或者使用object  .append(项),或者在批处理中使用object  .extend(list_of_items)。这主要用于列表子类,但是其他类可以使用它,只要它们有附加()和扩展()方法并具有适当的签名。(是否使用append()或extend()取决于使用哪个pickle协议版本以及要附加的项的数量,因此必须支持这两者。)
        可选地,迭代器(不是序列)产生连续的键值对。这些项将使用obj[key]  =值存储到对象中。这主要用于字典子类,但只要实现__setitem__(),其他类就可以使用它。

        object.__reduce_ex__(protocol)
        另外,一个__reduce_ex__()方法可以定义。唯一的区别是这个方法应该使用一个整型参数协议版本。在定义时,pickle将更喜欢它而不是__reduce__()方法。此外,__reduce__()自动成为扩展版本的同义词。此方法的主要用途是为较老的Python版本提供向后兼容的reduce值。  





猜你喜欢

转载自blog.csdn.net/www_rsqdz_net/article/details/79798862