Python对象序列化和反序列化pickle和marshal

在开展跨机器交互时,常常要在机器之间传递“对象”,而不仅仅是数据,这样我们就需要将Python对象进行序列化和反系列化,前面的json其实提供了一种序列化和反序列化的机制,但仅仅支持Python的简单对象,对于复杂的对象需要自己实现序列化和反序列化编码。

Python提供了一种内部格式的序列化和反序列化机制。

pickle

模块 pickle 实现了对一个 Python 对象结构的二进制序列化和反序列化。 "pickling" 是将 Python 对象及其所拥有的层次结构转化为一个字节流的过程,而 "unpickling" 是相反的操作,会将(来自一个 binary file 或者 bytes-like object 的)字节流转化回一个对象层次结构。 pickling(和 unpickling)也被称为“序列化”, “编组” 或者 “平面化”。而为了避免混乱,此处采用术语 “封存 (pickling)” 和 “解封 (unpickling)”。

与 json 模块的比较

在 pickle 协议和 JSON (JavaScript Object Notation) 之间有着本质上的差异:

  • JSON 是一个文本序列化格式(它输出 unicode 文本,尽管在大多数时候它会接着以 utf-8 编码),而 pickle 是一个二进制序列化格式;
  • JSON 是我们可以直观阅读的,而 pickle 不是;
  • JSON是可互操作的,在Python系统之外广泛使用,而pickle则是Python专用的;
  • 默认情况下,JSON 只能表示 Python 内置类型的子集,不能表示自定义的类;但 pickle 可以表示大量的 Python 数据类型(可以合理使用 Python 的对象内省功能自动地表示大多数类型,复杂情况可以通过实现 specific object APIs 来解决)。
  • 不像pickle,对一个不信任的JSON进行反序列化的操作本身不会造成任意代码执行漏洞。

各版本的差异

pickle 所使用的数据格式仅可用于 Python。默认情况下,pickle 格式使用相对紧凑的二进制来存储。如果需要让文件更小,可以高效地 压缩 由 pickle 封存的数据。pickletools 模块包含了相应的工具用于分析 pickle 生成的数据流。

当前共有 6 种不同的协议可用于封存操作。 使用的协议版本越高,读取所生成 pickle 对象所需的 Python 版本就要越新。

  • v0 版协议是原始的“人类可读”协议,并且向后兼容早期版本的 Python。
  • v1 版协议是较早的二进制格式,它也与早期版本的 Python 兼容。
  • 第 2 版协议是在 Python 2.3 中引入的。 它为 新式类 提供了更高效的封存机制。 请参考 PEP 307 了解第 2 版协议带来的改进的相关信息。
  • v3 版协议是在 Python 3.0 中引入的。 它显式地支持 bytes 字节对象,不能使用 Python 2.x 解封。这是 Python 3.0-3.7 的默认协议。
  • v4 版协议添加于 Python 3.4。它支持存储非常大的对象,能存储更多种类的对象,还包括一些针对数据格式的优化。它是Python 3.8使用的默认协议。有关第 4 版协议带来改进的信息,请参阅 PEP 3154。
  • 第 5 版协议是在 Python 3.8 中加入的。 它增加了对带外数据的支持,并可加速带内数据处理。 请参阅 PEP 574 了解第 5 版协议所带来的改进的详情。

序列化

序列化为bytes

pickle.dumps(objprotocol=None*fix_imports=Truebuffer_callback=None)

将obj对象序列化为bytes对象。

参数说明:

  • obj:待序列化的对象
  •  protocol:告知 pickler 使用指定的协议,可选择的协议范围从 0 到 HIGHEST_PROTOCOL。如果没有指定,这一参数默认值为 DEFAULT_PROTOCOL。指定一个负数就相当于指定 HIGHEST_PROTOCOL。
  • fix_imports:如果 fix_imports 为 True 且 protocol 小于 3,pickle 将尝试将 Python 3 中的新名称映射到 Python 2 中的旧模块名称,因此 Python 2 也可以读取封存的数据流。
  • buffer_callback:如果 buffer_callback 为 None(默认情况),缓冲区视图(buffer view)将会作为 pickle 流的一部分被序列化到 file 中。如果 buffer_callback 不为 None,那它可以用缓冲区视图调用任意次。如果某次调用返回了 False 值(例如 None),则给定的缓冲区是 带外的,否则缓冲区是带内的(例如保存在了 pickle 流里面)。如果 buffer_callback 不是 None 且 protocol 是 None 或小于 5,就会出错。
import pickle

class Base:
    def __init__(self, id, desc):
        self.id = id
        self.desc = desc

    def foo(self):
        print(f'Base {self.id=}, {self.desc=}')

IDSTART = 1100001

class Teacher(Base):
    def __init__(self, name, course):
        self.name = name
        self.course = course

class Student(Base):
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.score = {'Chinese':90, 'Math':95, 'English':90}
        self.tearchers = [Teacher('Chen', 'Chinese'), Teacher('Wang', 'Math'), Teacher('Su', 'English')]
        global IDSTART
        IDSTART += 1
        super().__init__(IDSTART, 'Student ' + name)

    def foo(self):
        print(f'Student {self.name=}, {self.age=}')


s1 = Student('John', 15)
ps1 = pickle.dumps(s1)
#print(ps1)
s2 = pickle.loads(ps1)
s2.foo() #Student self.name='John', self.age=15

序列化为文件

pickle.dump(objfileprotocol=None*fix_imports=Truebuffer_callback=None)

将序列化写入文件中。

序列化对象Pickler

class pickle.Pickler(fileprotocol=None*fix_imports=Truebuffer_callback=None)

它接受一个二进制文件用于写入 pickle 数据流。

主要方法:

dump(obj)

将 obj 封存后的内容写入已打开的文件对象,该文件对象已经在构造函数中指定。

persistent_id(obj)
默认无动作,子类继承重载时使用。
如果 persistent_id() 返回 None,obj 会被照常 pickle。如果返回其他值,Pickler 会将这个函数的返回值作为 obj 的持久化 ID(Pickler 本应得到序列化数据流并将其写入文件,若此函数有返回值,则得到此函数的返回值并写入文件)。这个持久化 ID 的解释应当定义在 Unpickler.persistent_load() 中(该方法定义还原对象的过程,并返回得到的对象)。注意,persistent_id() 的返回值本身不能拥有持久化 ID。

dispatch_table
Pickler 对象的 dispatch 表是 copyreg.pickle() 中用到的 reduction 函数 的注册。dispatch 表本身是一个 class 到其 reduction 函数的映射键值对。一个 reduction 函数只接受一个参数,就是其关联的 class,函数行为应当遵守 __reduce__() 接口规范。
Pickler 对象默认并没有 dispatch_table 属性,该对象默认使用 copyreg 模块中定义的全局 dispatch 表。如果要为特定 Pickler 对象自定义序列化过程,可以将 dispatch_table 属性设置为类字典对象(dict-like object)。另外,如果 Pickler 的子类设置了 dispatch_table 属性,则该子类的实例会使用这个表作为默认的 dispatch 表。

reducer_override(obj)
可以在 Pickler 的子类中定义的特殊 reducer。此方法的优先级高于 dispatch_table 中的任何 reducer。它应该与 __reduce__() 方法遵循相同的接口,它也可以返回 NotImplemented,这将使用 dispatch_table 里注册的 reducer 来封存 obj。

序列化缓冲区对象PickleBuffer

class pickle.PickleBuffer(buffer)

缓冲区的包装器 (wrapper),缓冲区中包含着可封存的数据。buffer 必须是一个 buffer-providing 对象,比如 bytes-like object 或多维数组。
PickleBuffer 本身就可以生成缓冲区对象,因此可以将其传递给需要缓冲区生成器的其他 API,比如 memoryview。
PickleBuffer 对象只能用 pickle 版本 5 及以上协议进行序列化。它们符合 带外序列化 的条件。

主要方法:

raw()
返回该缓冲区底层内存区域的 memoryview。 返回的对象是一维的、C 连续布局的 memoryview,格式为 B (无符号字节)。 如果缓冲区既不是 C 连续布局也不是 Fortran 连续布局的,则抛出 BufferError 异常。

release()
释放由 PickleBuffer 占用的底层缓冲区。

反序列化

从bytes反序列化

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

将data反序列化为一个python对象

参数说明:

  • data:待反序列化的串,必须是bytes串
  • 可选的参数是 fix_imports, encoding 和 errors,用于控制由Python 2 生成的 pickle 流的兼容性。如果 fix_imports 为 True,则 pickle 将尝试将旧的 Python 2 名称映射到 Python 3 中对应的新名称。encoding 和 errors 参数告诉 pickle 如何解码 Python 2 存储的 8 位字符串实例;这两个参数默认分别为 'ASCII' 和 'strict'。encoding 参数可置为 'bytes' 来将这些 8 位字符串实例读取为字节对象。读取 NumPy array 和 Python 2 存储的 datetime、date 和 time 实例时,请使用 encoding='latin1'。
  • 如果 buffers 为 None(默认值),则反序列化所需的所有数据都必须包含在 pickle 流中。这意味着在实例化 Pickler 时(或调用 dump() 或 dumps() 时),参数 buffer_callback 为 None。如果 buffers 不为 None,则每次 pickle 流引用 带外 缓冲区视图时,消耗的对象都应该是可迭代的启用缓冲区的对象。这样的缓冲区应该按顺序地提供给 Pickler 对象的 buffer_callback 方法。

从文件反序列化

pickle.load(file*fix_imports=Trueencoding='ASCII'errors='strict'buffers=None)

从文件对象中读取数据进行反序列化。

反序列化对象Unpickler

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

它接受一个二进制文件用于读取 pickle 数据流。

主要方法:

load()
从构造函数中指定的文件对象里读取封存好的对象,重建其中特定对象的层次结构并返回。封存对象以外的其他字节将被忽略。

persistent_load(pid)
默认抛出 UnpicklingError 异常。
如果定义了此方法,persistent_load() 应当返回持久化 ID pid 所指定的对象。 如果遇到无效的持久化 ID,则应当引发 UnpicklingError。

find_class(module, name)
如有必要,导入 module 模块并返回其中名叫 name 的对象,其中 module 和 name 参数都是 str 对象。注意,不要被这个函数的名字迷惑, find_class() 同样可以用来导入函数。
子类可以重载此方法,来控制加载对象的类型和加载对象的方式,从而尽可能降低安全风险。

相关的魔术方法

通常,使一个实例可被封存不需要附加任何代码。Pickle 默认会通过 Python 的内省机制获得实例的类及属性。而当实例解封时,它的 __init__() 方法通常 不会 被调用。其默认动作是:先创建一个未初始化的实例,然后还原其属性。

import pickle

class Base:
    def __init__(self, id, desc):
        self.id = id
        self.desc = desc

    def foo(self):
        print(f'Base {self.id=}, {self.desc=}')

IDSTART = 1100001

class Teacher(Base):
    def __init__(self, name, course):
        self.name = name
        self.course = course

class Student(Base):
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.score = {'Chinese':90, 'Math':95, 'English':90}
        self.tearchers = [Teacher('Chen', 'Chinese'), Teacher('Wang', 'Math'), Teacher('Su', 'English')]
        global IDSTART
        IDSTART += 1
        super().__init__(IDSTART, 'Student ' + name)
        print(f'Student init {name=}, {age=}')

    def __new__(cls, *args, **kwargs):
        print(f'Student new')
        return super().__new__(cls)

    def foo(self):
        print(f'Student {self.name=}, {self.age=}')


s1 = Student('John', 15)
ps1 = pickle.dumps(s1)
#print(ps1)
s2 = pickle.loads(ps1)
s2.foo()

‘’'
Student new
Student init name='John', age=15
Student new
Student self.name='John', self.age=15
‘''

上面的例子也可以看出,只输出一次__init__()方法,在反序列化时,对象并不会再次调用__init__()方法。而__new__()方法会被再次被调用。

__getnewargs__()和__getnewargs_ex__()

对于使用第 2 版或更高版协议的 pickle,实现了 __getnewargs_ex__() 方法的类可以控制在解封时传给 __new__() 方法的参数。本方法必须返回一对 (args, kwargs) 用于构建对象,其中 args 是表示位置参数的 tuple,而 kwargs 是表示命名参数的 dict。它们会在解封时传递给 __new__() 方法。

如果类的 __new__() 方法只接受关键字参数,则应当实现这个方法。否则,为了兼容性,更推荐实现 __getnewargs__() 方法。

import pickle

class Base:
    def __init__(self, id, desc):
        self.id = id
        self.desc = desc

    def foo(self):
        print(f'Base {self.id=}, {self.desc=}')

IDSTART = 1100001

class Teacher(Base):
    def __init__(self, name, course):
        self.name = name
        self.course = course

class Student(Base):
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.score = {'Chinese':90, 'Math':95, 'English':90}
        self.tearchers = [Teacher('Chen', 'Chinese'), Teacher('Wang', 'Math'), Teacher('Su', 'English')]
        global IDSTART
        IDSTART += 1
        super().__init__(IDSTART, 'Student ' + name)
        print(f'Student init {name=}, {age=}')

    def __new__(cls, *args):
        print(f'Student new')

        return super().__new__(cls)

    def __getnewargs__(self):
        print(f'__getnewargs__')
        return (0,0)

    def foo(self):
        print(f'Student {self.name=}, {self.age=}')


s1 = Student('John', 15)
ps1 = pickle.dumps(s1)
#print(ps1)
s2 = pickle.loads(ps1)
s2.foo()

’’’
Student new
Student init name='John', age=15
__getnewargs__
Student new
Student self.name='John', self.age=15
‘’‘

__getstate__()

类还可以通过重载方法 __getstate__() 来进一步影响它们的实例要如何被封存。 该方法将被调用并且其返回的对象会被当作实例的内容来封存,而不是使用默认状态。 这有几种情况:

  • 对于没有实例 __dict__ 以及没有 __slots__ 的类,默认状态为 None。
  • 对于具有实例 __dict__ 而没有 __slots__ 的类,默认状态为 self.__dict__。
  • 对于具有实例 __dict__ 和 __slots__ 的类,默认状态为一个由两个字典: self.__dict__、以及将槽位名称映射到槽位值的字典所组成的元组。 只有包含具体值的槽位才会被包括在后一个字典当中。
  • 对于具有 __slots__ 而没有实例 __dict__ 的类,默认状态为一个第一项是 None 而第二项是上述将槽位名称映射到槽位值的字典的元组。

__setstate__(state)

当解封时,如果类定义了 __setstate__(),就会在已解封状态下调用它。此时不要求实例的 state 对象必须是 dict。没有定义此方法的话,先前封存的 state 对象必须是 dict,且该 dict 内容会在解封时赋给新实例的 __dict__。

如果 __getstate__() 返回 False,那么在解封时就不会调用 __setstate__() 方法。

下面的示例展示了如何修改类在封存时的行为。其中 TextReader 类打开了一个文本文件,每次调用其 readline() 方法则返回行号和该行的字符。 在封存这个 TextReader 的实例时,除了 文件对象,其他属性都会被保存。 当解封实例时,需要重新打开文件,然后从上次的位置开始继续读取。实现这些功能需要实现 __setstate__() 和 __getstate__() 方法。

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!'

__reduce__()

__reduce__() 方法不带任何参数,并且应返回字符串或一个元组(返回的对象通常称为“reduce 值”)。
如果返回字符串,该字符串会被当做一个全局变量的名称。它应该是对象相对于其模块的本地名称,pickle 模块会搜索模块命名空间来确定对象所属的模块。这种行为常在单例模式使用。
如果返回的是元组,则应当包含 2 到 6 个元素,可选元素可以省略或设置为 None。每个元素代表的意义如下:

  1. 一个可调用对象,该对象会在创建对象的最初版本时调用。可调用对象的参数,是一个元组。如果可调用对象不接受参数,必须提供一个空元组。
  2. 可选元素,用于表示对象的状态,将被传给前述的 __setstate__() 方法。 如果对象没有此方法,则这个元素必须是字典类型,并会被添加至 __dict__ 属性中。
  3. 可选元素,一个返回连续项的迭代器(而不是序列)。这些项会被 obj.append(item) 逐个加入对象,或被 obj.extend(list_of_items) 批量加入对象。这个元素主要用于 list 的子类,也可以用于那些正确实现了 append() 和 extend() 方法的类。(具体是使用 append() 还是 extend() 取决于 pickle 协议版本以及待插入元素的项数,所以这两个方法必须同时被类支持。)
  4. 可选元素,一个返回连续键值对的迭代器(而不是序列)。这些键值对将会以 obj[key] = value 的方式存储于对象中。该元素主要用于 dict 子类,也可以用于那些实现了 __setitem__() 的类。
  5. 可选元素,一个带有 (obj, state) 签名的可调用对象。该可调用对象允许用户以编程方式控制特定对象的状态更新行为,而不是使用 obj 的静态 __setstate__() 方法。如果此处不是 None,则此可调用对象的优先级高于 obj 的 __setstate__()。

__reduce_ex__(protocol)

作为替代选项,也可以实现 __reduce_ex__() 方法。 此方法的唯一不同之处在于它应接受一个整型参数用于指定协议版本。 如果定义了这个函数,则会覆盖 __reduce__() 的行为。 此外,__reduce__() 方法会自动成为扩展版方法的同义词。 这个函数主要用于为以前的 Python 版本提供向后兼容的 reduce 值。

持久化外部对象

为了获取对象持久化的利益, pickle 模块支持引用已封存数据流之外的对象。 这样的对象是通过一个持久化 ID 来引用的,它应当是一个由字母数字类字符组成的字符串 (对于第 0 版协议) 5 或是一个任意对象 (用于任意新版协议)。
pickle 模块不提供对持久化 ID 的解析工作,它将解析工作分配给用户定义的方法,分别是 pickler 中的 persistent_id() 方法和 unpickler 中的 persistent_load() 方法。
要通过持久化 ID 将外部对象封存,必须在 pickler 中实现 persistent_id() 方法,该方法接受需要被封存的对象作为参数,返回一个 None 或返回该对象的持久化 ID。如果返回 None,该对象会被按照默认方式封存为数据流。如果返回字符串形式的持久化 ID,则会封存这个字符串并加上一个标记,这样 unpickler 才能将其识别为持久化 ID。
要解封外部对象,Unpickler 必须实现 persistent_load() 方法,接受一个持久化 ID 对象作为参数并返回一个引用的对象。

# 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()

dispatch_table

如果想对某些类进行自定义封存,而又不想在类中增加用于封存的代码,就可以创建带有特殊 dispatch 表的 pickler。
在 copyreg 模块的 copyreg.dispatch_table 中定义了全局 dispatch 表。因此,可以使用 copyreg.dispatch_table 修改后的副本作为自有 dispatch 表。

下面的例子创建了一个带有自有 dispatch 表的 pickle.Pickler 实例,它可以对 SomeClass 类进行特殊处理:

f = io.BytesIO()
p = pickle.Pickler(f)
p.dispatch_table = copyreg.dispatch_table.copy()
p.dispatch_table[SomeClass] = reduce_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 模块的所有用户共享的全局分发表。

 

reducer_override() 

有时,dispatch_table 可能不够灵活。 特别是当我们想要基于对象类型以外的其他规则来对封存进行定制,或是当我们想要对函数和类的封存进行定制的时候。
对于那些情况,可能要基于 Pickler 类进行子类化并实现 reducer_override() 方法。 此方法可返回任意的归约元组 (参见 __reduce__())。 它也可以选择返回 NotImplemented 来回退到传统行为。
如果同时定义了 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

marshal

此模块包含一此能以二进制格式来读写 Python 值的函数。 这种格式是 Python 专属的,但是独立于特定的机器架构(即你可以在一台 PC 上写入某个 Python 值,将文件传到一台 Sun 上并在那里读取它)。 这种格式的细节有意不带文档说明;它可能在不同 Python 版本中发生改变(但这种情况极少发生)。

这不是一个通用的“持久化”模块。 对于通用的持久化以及通过 RPC 调用传递 Python 对象,请参阅 pickle 和 shelve 等模块。 marshal 模块主要是为了支持读写 .pyc 文件形式“伪编译”代码的 Python 模块。 因此,Python 维护者保留在必要时以不向下兼容的方式修改 marshal 格式的权利。 如果你要序列化和反序列化 Python 对象,请改用 pickle 模块 -- 其执行效率相当,版本独立性有保证,并且 pickle 还支持比 marshal 更多样的对象类型。

不是所有 Python 对象类型都受支持;一般来说,此模块只能写入和读取不依赖于特定 Python 调用的对象。 下列类型是受支持的:布尔值、整数、浮点数、复数、字符串、字节串、字节数组、元组、列表、集合、冻结集合、字典和代码对象,需要了解的一点是元组、列表、集合、冻结集合和字典只在其所包含的值也是这些值时才受支持。 单例对象 None, Ellipsis and StopIteration 也可以被 marshal 和 unmarshal。 对于 version 低于 3 的格式,递归列表、集合和字典无法被写入。

此模块的名称来源于 Modula-3 (及其他语言) 的设计者所使用的术语,他们使用术语 "marshal" 来表示以自包含的形式传输数据。 严格地说,将数据从内部形式转换为外部形式 (例如用于 RPC 缓冲区) 称为 "marshal" 而其逆过程则称为 "unmarshal"。

将对象写入文件

marshal.dump(valuefile[, version])

向打开的文件写入值。 值必须为受支持的类型。 文件必须为可写的 binary file。
如果值具有(或所包含的对象具有)不受支持的类型,则会引发 ValueError --- 但是将向文件写入垃圾数据。 对象也将不能正确地通过 load() 重新读取。
version 参数指明 dump 应当使用的数据格式

从文件中获取对象

marshal.load(file)

从打开的文件读取一个值并返回。 如果读不到有效的值(例如由于数据为不同 Python 版本的不兼容 marshal 格式),则会引发 EOFError, ValueError 或 TypeError。 文件必须为可读的 binary file。

dump对象为字节串

marshal.dumps(value[, version])

返回将通过 dump(value, file) 被写入一个文件的字节串对象。 值必须属于受支持的类型。 如果值属于(或包含的对象属于)不受支持的类型则会引发 ValueError。
version 参数指明 dumps 应当使用的数据类型

从字节串装载对象

marshal.loads(bytes)

将 bytes-like object 转换为一个值。 如果找不到有效的值,则会引发 EOFError, ValueError 或 TypeError。 输入的额外字节串会被忽略。

marshal.version

指明模块所使用的格式。 第 0 版为历史格式,第 1 版为共享固化的字符串,第 2 版对浮点数使用二进制格式。 第 3 版添加了对于对象实例化和递归的支持。 目前使用的为第 4 版。

你可能感兴趣的:(python,开发语言)