📜  pickle — Python对象序列化

📅  最后修改于: 2020-04-16 14:02:06             🧑  作者: Mango

pickle模块用于实现二进制协议,以对Python对象结构进行序列化和反序列化。

  • Pickling:这是一个将Python对象层次结构转换为字节流的过程。
  • Unpickling:这是Pickling过程的逆过程,其中字节流被转换为对象层次结构。

模块接口:

    • dumps():调用此函数以序列化对象层次结构。
    • loads():调用此函数以反序列化数据流。

为了更好地控制序列化和反序列化,将分别创建Pickler或Unpickler对象。
pickle模块提供的常量:

  1. pickle.HIGHEST_PROTOCOL
    这是一个整数值,代表可用的最高协议版本。这被视为传递给函数dumps()的协议值。
  2. pickle.DEFAULT_PROTOCOL
    这是一个整数值,表示用于Pickling的默认协议,其值可能小于最高协议的值。

pickle模块提供的函数:

  1. pickle.dump(obj,file,protocol = None,*,fix_imports = True)
    此函数等效于Pickler(file,protocol).dump(obj)。这用于将obj的二进制表示形式写入目标文件。可选的协议参数是一个整数,告诉选择器使用给定的协议。支持的协议为0到HIGHEST_PROTOCOL。如果未指定,则默认值为DEFAULT_PROTOCOL。如果指定负数,则选择HIGHEST_PROTOCOL。
    如果fix_imports为true并且协议小于3,则pickle将尝试将新的Python 3名称映射到Python 2中使用的旧模块名称,以便pickle数据流可被Python 2读取。
    示例:
    # Python程序来说明pickle.dump()
    import pickle
    from StringIO import StringIO
    class SimpleObject(object):
        def __init__(self, name):
            self.name = name
            l = list(name)
            l.reverse()
            self.name_backwards = ''.join(l)
            return
    data = []
    data.append(SimpleObject('pickle'))
    data.append(SimpleObject('cPickle'))
    data.append(SimpleObject('last'))
    # 用StringIO模拟文件
    out_s = StringIO()
    # 写入流
    for o in data:
        print 'WRITING: %s (%s)' % (o.name, o.name_backwards)
        pickle.dump(o, out_s)
        out_s.flush()

    输出:

    WRITING: pickle (elkcip)
    WRITING: cPickle (elkciPc)
    WRITING: last (tsal)

    2,pickle.dumps(obj,protocol = None,*,fix_imports = True)
    此函数以字节对象形式返回对象的pickle表示形式。
    范例:

    # Python程序来说明Picle.dumps()
    import pickle
    data = [ { 'a':'A', 'b':2, 'c':3.0 } ]
    data_string = pickle.dumps(data)
    print 'PICKLE:', data_string

    输出:

    PICKLE: (lp0
    (dp1
    S'a'
    p2
    S'A'
    p3
    sS'c'
    p4
    F3.0
    sS'b'
    p5
    I2
    sa.
    

    3,pickle.load(file,*,fix_imports = True,encoding=“ ASCII”,errors =“严格”)
    此函数等效于Unpickler(file).load()。此函数用于从打开的目标文件中读取pickle的对象表示形式,并返回指定的重构对象层次结构。
    范例: 

    # Python程序来说明pickle.load()
    import pickle
    from StringIO import StringIO
    class SimpleObject(object):
        def __init__(self, name):
            self.name = name
            l = list(name)
            l.reverse()
            self.name_backwards = ''.join(l)
            return
    data = []
    data.append(SimpleObject('pickle'))
    data.append(SimpleObject('cPickle'))
    data.append(SimpleObject('last'))
    # 用StringIO模拟文件
    out_s = StringIO()
    # 写入流
    for o in data:
        print 'WRITING: %s (%s)' % (o.name, o.name_backwards)
        pickle.dump(o, out_s)
        out_s.flush()
    # 设置可读流
    in_s = StringIO(out_s.getvalue())
    # 读取数据
    while True:
        try:
            o = pickle.load(in_s)
        except EOFError:
            break
        else:
            print 'READ: %s (%s)' % (o.name, o.name_backwards)

    输出: 

    WRITING: pickle (elkcip)
    WRITING: cPickle (elkciPc)
    WRITING: last (tsal)
    READ: pickle (elkcip)
    READ: cPickle (elkciPc)
    READ: last (tsal)

     4,pickle.loads(bytes_object,*,fix_imports = True,encoding=“ ASCII”,errors =“严格”)
    此函数用于从字节对象读取pickle的对象表示形式,并返回指定的重构对象层次结构。

    范例:

    # Python程序来说明pickle.loads()
    import pickle
    import pprint
    data1 = [ { 'a':'A', 'b':2, 'c':3.0 } ]
    print 'BEFORE:',
    pprint.pprint(data1)
    data1_string = pickle.dumps(data1)
    data2 = pickle.loads(data1_string)
    print 'AFTER:',
    pprint.pprint(data2)
    print 'SAME?:', (data1 is data2)
    print 'EQUAL?:', (data1 == data2)

    输出:

    BEFORE:[{'a': 'A', 'b': 2, 'c': 3.0}]
    AFTER:[{'a': 'A', 'b': 2, 'c': 3.0}]
    SAME?: False
    EQUAL?: True

    pickle模块提供的异常:

    1. pickle.PickleError异常
      此异常继承Exception。它是pickle中引发的所有其他异常的基类。
    2. pickle.PicklingError异常
      此异常继承PickleError。当Pickler遇到无法拾取的对象时,将引发此异常。
    3. pickle.UnpicklingError
      异常继承此PickleError。当解开对象时出现数据损坏或安全冲突之类的问题时,会引发此异常。

    pickle模块导出的类:

    1. class pickle.Pickler(file,protocol = None,*,fix_imports = True)
      此类使用一个二进制文件来编写一个pickle数据流。
      1. dump(obj): 此函数用于将obj的pickle表示形式写入构造函数中提供的打开文件对象。
      2. sistence_id(obj): 如果persistent_id()返回None,则照常对obj进行pickle。它的任何子类都可以重写它。
      3. Dispatch_table :  Pickler对象的调度表是一个映射,其键为类,其值为归约函数。
        默认情况下,pickler对象将不具有dispatch_table属性,而是使用由copyreg模块管理的全局调度表。示例:下面的代码创建一个带专用调度表的pickle.Pickler实例,该调度表专门处理SomeClass类。
        f = io.BytesIO()
        p = pickle.Pickler(f)
        p.dispatch_table = copyreg.dispatch_table.copy()
        p.dispatch_table[SomeClass] = reduce_SomeClass
      4. Fast: Fast通过不生成多余的PUT操作码来禁用备忘录的使用并加快pickle过程。

2, 类 pickle.Unpickler(file,*,fix_imports = True,encoding=“ ASCII”,errors=“ strict”)。

此类使用一个二进制文件来读取泡pickle据流。

    1. load(): 此函数用于从打开的目标文件中读取一个pickle的对象表示形式,并返回指定的重构对象层次结构。
    2. sistence_load (pid):默认情况下会引发UnpicklingError。
    3. find_class(module,name):该函数在需要时导入模块,并从中返回名为name的对象,其中module和name参数是str对象。

什么可以pickle和不可pickle?
可以pickle以下类型:

  • None, True, and False
  • 整数,浮点数,复数
  • 字符串,字节,字节数组
  • 仅包含可pickle对象的元组,列表,集合和词典
  • 在模块顶层定义的功能(使用def,而不是lambda)
  • 在模块顶层定义的内置函数
  • 在模块顶层定义的类
  • 此类的实例,它们的__dict__或调用__getstate __()的结果是可pickle

pickle类实例:
本节说明可用于定义,自定义和控制如何pickleunpickle类实例的通用机制。
无需其他代码即可使实例可pickle。默认情况下,pickle将通过自省检索实例的类和属性。
类可以通过提供一种或几种特殊方法来更改默认行为:

  1. object .__ getnewargs_ex __()
    此方法规定了取消pickle时传递给__new __()方法的值。该方法必须返回一对(args,kwargs),其中args是位置参数的元组,而kwargs是用于构造对象的命名参数的字典。
  2. object .__ getnewargs __()
    此方法仅支持正参数。它必须返回参数args的元组,该参数将在unpickle后传递给__new __()方法。
  3. object .__ getstate __()
    如果此方法是由类定义的,则将调用它,并将返回的对象pickle为实例的内容,而不是实例的字典的内容。
  4. object .__ setstate __(state)
    如果此方法是由类定义的,则以unpickle状态调用它。腌制状态必须是字典,并且其项已分配给新实例的字典。
  5. object .__ reduce __()__
    reduce __()方法不带任何参数,并且应返回字符串,最好返回一个元组。
  6. object .__ reduce_ex __(protocol)
    此方法类似于__reduce__方法。它需要一个整数参数。该方法的主要用途是为较旧的Python版本提供向后兼容的reduce值。

示例:处理有状态对象
此示例说明如何修改类的pickle行为。TextReader类打开一个文本文件,并在每次调用其readline()方法时返回行号和行内容。

  1. 如果pickle了TextReader实例,则将保存除文件对象成员之外的所有属性。
  2. 取消实例化后,将重新打开文件,并从最后一个位置恢复读取。
    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):
            # 从self .__ dict__复制对象的状态,该对象包含我们所有的实例属性。
            # 始终使用dict.copy()方法以避免修改原始状态。
            state = self.__dict__.copy()
            # 删除不可拾取的条目.
            del state['file']
            return state
        def __setstate__(self, state):
            # 恢复实例属性(即文件名和行号).
            self.__dict__.update(state)
            # 恢复先前打开的文件的状态。为此,我们需要重新打开它并从中读取直到恢复行数。
            file = open(self.filename)
            for _ in range(self.lineno):
                file.readline()
            # 最后,保存文件.
            self.file = file
    reader = TextReader("hello.txt")
    print(reader.readline())
    print(reader.readline())
    new_reader = pickle.loads(pickle.dumps(reader))
    print(new_reader.readline())

    输出:

    '1: Hello world!'
    '2: I am line number two.'
    '3: Goodbye!