首页 > 编程语言 > 详细

Python3标准库:pickle对象串行化

时间:2020-03-22 14:02:44      阅读:49      评论:0      收藏:0      [点我收藏+]

1. pickle对象串行化

pickle模块实现了一个算法可以将任意的Python对象转换为一系列字节。这个过程也被称为串行化对象。可以传输或存储表示对象的字节流,然后再重新构造来创建有相同性质的新对象。

1.1 编码和解码字符串中的数据

第一个例子使用dumps()将一个数据结构编码为一个字符串,然后把这个字符串打印到控制台。它使用了一个完全由内置类型构成的数据结构。任何类的实例都可以pickled,如后面的例子所示。

import pickle
import pprint

data = [{a: A, b: 2, c: 3.0}]
print(DATA:, end= )
pprint.pprint(data)

data_string = pickle.dumps(data)
print(PICKLE: {!r}.format(data_string))

默认的,pickle将以一种二进制格式写入,在Python 3程序之间共享时这种格式兼容性最好。

技术分享图片

数据串行化后,可以写到一个文件、套接字、管道或者其他位置。之后可以读取这个文件,将数据解除pickled,以便用同样的值构造一个新对象。

import pickle
import pprint

data1 = [{a: A, b: 2, c: 3.0}]
print(BEFORE: , end= )
pprint.pprint(data1)

data1_string = pickle.dumps(data1)

data2 = pickle.loads(data1_string)
print(AFTER : , end= )
pprint.pprint(data2)

print(SAME? :, (data1 is data2))
print(EQUAL?:, (data1 == data2))

新构造的对象等于原来的对象,但并不是同一个对象。

技术分享图片

1.2 处理流

除了dumps()和loads(),pickle还提供了一些便利函数来处理类似文件的流。可以向一个流写多个对象,然后从流读取这些对象,而无须事先知道要写多少个对象或者这些对象多大。

import io
import pickle

class SimpleObject:

    def __init__(self, name):
        self.name = name
        self.name_backwards = name[::-1]
        return

data = []
data.append(SimpleObject(pickle))
data.append(SimpleObject(preserve))
data.append(SimpleObject(last))

# Simulate a file.
out_s = io.BytesIO()

# Write to the stream
for o in data:
    print(WRITING : {} ({}).format(o.name, o.name_backwards))
    pickle.dump(o, out_s)
    out_s.flush()

# Set up a read-able stream
in_s = io.BytesIO(out_s.getvalue())

# Read the data
while True:
    try:
        o = pickle.load(in_s)
    except EOFError:
        break
    else:
        print(READ    : {} ({}).format(
            o.name, o.name_backwards))

这个例子使用两个BytesIO缓冲区来模拟流。第一个缓冲区接收pickled的对象,它的值被填入第二个缓冲区,load()读取这个缓冲区。简单的数据库格式也可以使用pickle来存储对象。shelve模块就是这样一个实现。

技术分享图片

除了存储数据,pickle对于进程间通信也很方便。例如,os.fork()和os.pipe()可以用来建立工作进程,从一个管道读取作业指令,并把结果写至另一个管道。管理工作线程池以及发送作业和接收响应的核心代码可以重用,因为作业和响应对象不必基于一个特定的类。使用管道或套接字时,在转储各个对象之后不要忘记刷新输出,以便将数据通过连接推送到另一端。参见multiprocessing模块来了解一个可重用的工作线程池管理器。

1.3 重构对象的问题

处理定制类时,pickled的类必须出现在读取pickle的进程所在的命名空间里。只会pickled这个实例的数据,而不是类定义。类名用于查找构造函数,以便在解除pickled时参见新对象。下面这个例子将一个类的实例写至一个文件。

import pickleclass SimpleObject:

    def __init__(self, name):
        self.name = name
        l = list(name)
        l.reverse()
        self.name_backwards = ‘‘.join(l)

if __name__ == __main__:
    data = []
    data.append(SimpleObject(pickle))
    data.append(SimpleObject(preserve))
    data.append(SimpleObject(last))

    with open(Test.py, wb) as out_s:
        for o in data:
            print(WRITING: {} ({}).format(
                o.name, o.name_backwards))
            pickle.dump(o, out_s)

运行这个脚本时,会根据作为命令行参数给定的名字来创建一个文件。

技术分享图片

通过简单的尝试加载而得到的pickled对象将会失败。 

import pickle

with open(Test.py, rb) as in_s:
    while True:
        try:
            o = pickle.load(in_s)
        except EOFError:
            break
        else:
            print(READ: {} ({}).format(
                o.name, o.name_backwards))

这个版本失败的原因在于并没有SimpleObject类。

技术分享图片

修正后的版本从原脚本导入了SimpleObject,这一次运行会成功。在导入列表的最后增加了import语句后,现在脚本就能找到这个类并构造对象了。 

from demo import SimpleObject

现在允许修改后的脚本会生成期望的结果。

技术分享图片

1.4 Unpicklable的对象 

并不是所有对象都是可pickled的。套接字、文件句柄、数据库连接以及其他运行时状态依赖于操作系统或其他进程的对象,其可能无法用一种有意义的方式保存。如果对象包含不可pickled的属性,则可以定义__getstate__()和__setstate__()来返回所pickled实例的状态的一个子集。

__getstate__()方法必须返回一个对象,其中包含所pickled对象的内部状态。表示状态的一种便利方式是使用字典,不过值可以是任意的可pickled对象。保存状态,然后再从pickle加载对象时将所保存的状态传入__setstate__()。

import pickle

class State:

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return State({!r}).format(self.__dict__)

class MyClass:

    def __init__(self, name):
        print(MyClass.__init__({}).format(name))
        self._set_name(name)

    def _set_name(self, name):
        self.name = name
        self.computed = name[::-1]

    def __repr__(self):
        return MyClass({!r}) (computed={!r}).format(
            self.name, self.computed)

    def __getstate__(self):
        state = State(self.name)
        print(__getstate__ -> {!r}.format(state))
        return state

    def __setstate__(self, state):
        print(__setstate__({!r}).format(state))
        self._set_name(state.name)

inst = MyClass(name here)
print(Before:, inst)

dumped = pickle.dumps(inst)

reloaded = pickle.loads(dumped)
print(After:, reloaded)

这个例子使用了一个单独的State对象来保存MyClass的内部状态。从pickle加载MyClass的一个实例时,会向__setstate__()传入一个State实例,用来初始化这个对象。

技术分享图片

1.5 循环引用

pickle协议会自动处理对象之间的循环引用,所以复杂数据结构不需要任何特殊的处理。 

import pickle

class Node:
    """A simple digraph
    """
    def __init__(self, name):
        self.name = name
        self.connections = []

    def add_edge(self, node):
        "Create an edge between this node and the other."
        self.connections.append(node)

    def __iter__(self):
        return iter(self.connections)

def preorder_traversal(root, seen=None, parent=None):
    """Generator function to yield the edges in a graph.
    """
    if seen is None:
        seen = set()
    yield (parent, root)
    if root in seen:
        return
    seen.add(root)
    for node in root:
        recurse = preorder_traversal(node, seen, root)
        for parent, subnode in recurse:
            yield (parent, subnode)

def show_edges(root):
    "Print all the edges in the graph."
    for parent, child in preorder_traversal(root):
        if not parent:
            continue
        print({:>5} -> {:>2} ({}).format(
            parent.name, child.name, id(child)))

# Set up the nodes.
root = Node(root)
a = Node(a)
b = Node(b)
c = Node(c)

# Add edges between them.
root.add_edge(a)
root.add_edge(b)
a.add_edge(b)
b.add_edge(a)
b.add_edge(c)
a.add_edge(a)

print(ORIGINAL GRAPH:)
show_edges(root)

# Pickle and unpickle the graph to create
# a new set of nodes.
dumped = pickle.dumps(root)
reloaded = pickle.loads(dumped)

print(\nRELOADED GRAPH:)
show_edges(reloaded)

重新加载的节点并不是同一个对象,但保持了节点之间的关系,而且如果对象有多个引用,那么只会重新加载这个对象的一个副本。要验证这两点,可以在通过pickle传递节点之前和之后检查节点的id()值。

技术分享图片

Python3标准库:pickle对象串行化

原文:https://www.cnblogs.com/liuhui0308/p/12504654.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!