资讯专栏INFORMATION COLUMN

pickle和cPickle:Python对象的序列化(下)

LeviDing / 2719人阅读

摘要:重构对象的问题当与你自己的类一起工作时,你必须保证类被腌渍出现在读取的进程的命名空间中。因为使用值而不能被腌渍的类,可以定义和来返回状态的一个子集,才能被腌渍。腌渍和反腌渍该图来创建一个结点集合。

  

承接上文 pickle和cPickle:Python对象的序列化(上) 。

重构对象的问题

当与你自己的类一起工作时,你必须保证类被腌渍出现在读取pickle的进程的命名空间中。只有该实例的数据而不是类定义被腌渍。类名被用于在反腌渍时,找到构造器(constructor)以创建新对象。以此——往一个文件写入一个类的实例为例:

pythontry:
    import cPickle as pickle
except:
    import pickle
import sys

class SimpleObject(object):

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

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

    try:
        filename = sys.argv[1]
    except IndexError:
        raise RuntimeError("Please specify a filename as an argument to %s" % sys.argv[0])

    out_s = open(filename, "wb")
    try:
        # 写入流中
        for o in data:
            print "WRITING: %s (%s)" % (o.name, o.name_backwards)
            pickle.dump(o, out_s)
    finally:
        out_s.close()

在运行时,该脚本创建一个以在命令行指定的参数为名的文件:

python$ python pickle_dump_to_file_1.py test.dat

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

一个在读取结果腌渍对象失败的简化尝试:

pythontry:
    import cPickle as pickle
except:
    import pickle
import pprint
from StringIO import StringIO
import sys


try:
    filename = sys.argv[1]
except IndexError:
    raise RuntimeError("Please specify a filename as an argument to %s" % sys.argv[0])

in_s = open(filename, "rb")
try:
    # 读取数据
    while True:
        try:
            o = pickle.load(in_s)
        except EOFError:
            break
        else:
            print "READ: %s (%s)" % (o.name, o.name_backwards)
finally:
    in_s.close()

该版本失败的原因在于没有 SimpleObject 类可用:

python$ python pickle_load_from_file_1.py test.dat

Traceback (most recent call last):
  File "pickle_load_from_file_1.py", line 52, in 
    o = pickle.load(in_s)
AttributeError: "module" object has no attribute "SimpleObject"

正确的版本从原脚本中导入 SimpleObject ,可成功运行。
添加:

pythonfrom pickle_dump_to_file_1 import SimpleObject

至导入列表的尾部,接着重新运行该脚本:

python$ python pickle_load_from_file_2.py test.dat

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

当腌渍有值的数据类型不能被腌渍时(套接字、文件句柄(file handles)、数据库连接等之类的),有一些特别的考虑。因为使用值而不能被腌渍的类,可以定义 __getstate__()__setstate__() 来返回状态(state)的一个子集,才能被腌渍。新式类(New-style classes)也可以定义__getnewargs__(),该函数应当返回被传递至类内存分配器(the class memory allocator)(C.__new__())的参数。使用这些新特性的更多细节,包含在标准库文档中。

环形引用(Circular References)

pickle协议(pickle protocol)自动处理对象间的环形引用,因此,即使是很复杂的对象,你也不用特别为此做什么。考虑下面这个图:

上图虽然包括几个环形引用,但也能以正确的结构腌渍和重新读取(reloaded)。

pythonimport pickle

class Node(object):
    """
    一个所有结点都可知它所连通的其它结点的简单有向图。
    """
    def __init__(self, name):
        self.name = name
        self.connections = []
        return

    def add_edge(self, node):
        "创建两个结点之间的一条边。"
        self.connections.append(node)
        return

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

def preorder_traversal(root, seen=None, parent=None):
    """产生器(Generator )函数通过一个先根遍历(preorder traversal)生成(yield)边。"""
    if seen is None:
        seen = set()
    yield (parent, root)
    if root in seen:
        return
    seen.add(root)
    for node in root:
        for (parent, subnode) in preorder_traversal(node, seen, root):
            yield (parent, subnode)
    return

def show_edges(root):
    "打印图中的所有边。"
    for parent, child in preorder_traversal(root):
        if not parent:
            continue
        print "%5s -> %2s (%s)" % (parent.name, child.name, id(child))

# 创建结点。
root = Node("root")
a = Node("a")
b = Node("b")
c = Node("c")

# 添加边。
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)

# 腌渍和反腌渍该图来创建
# 一个结点集合。
dumped = pickle.dumps(root)
reloaded = pickle.loads(dumped)

print
print "RELOADED GRAPH:"
show_edges(reloaded)

重新读取的诸多节点(译者注:对应图中的圆圈)不再是同一个对象,但是节点间的关系保持住了,而且读取的仅仅是带有多个引用的对象的一个拷贝。上面所说的可以通过测试各节点在pickle处理前和之后的id()值来验证。

python$ python pickle_cycle.py

ORIGINAL GRAPH:
 root ->  a (4299721744)
    a ->  b (4299721808)
    b ->  a (4299721744)
    b ->  c (4299721872)
    a ->  a (4299721744)
 root ->  b (4299721808)

RELOADED GRAPH:
 root ->  a (4299722000)
    a ->  b (4299722064)
    b ->  a (4299722000)
    b ->  c (4299722128)
    a ->  a (4299722000)
 root ->  b (4299722064)
  

原文 pickle and cPickle – Python object serialization - Python Module of the Week 的后半部分。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/45345.html

相关文章

  • Python 对象列化——pickle and cPickle

    摘要:对象序列化从这篇文章粗略翻译的模块可以实现任意的对象转换为一系列字节即序列化对象的算法。的文档明确的表明它不提供安全保证。而利用则可以控制序列化的细节。 Python 对象序列化——pickle and cPickle 从这篇文章粗略翻译的pickle and cPickle pickle模块可以实现任意的Python对象转换为一系列字节(即序列化对象)的算法。这些字节流可以 被...

    Taonce 评论0 收藏0
  • picklecPicklePython对象列化(上)

    摘要:使用来创建一个表示该对象值的字符串。数据被序列化以后,你可以将它们写入文件套接字管道等等中。如果你使用管道或者套接字,在通过连至另一端的连接倾倒所有对象推送数据之后,别忘了冲洗。 目的:Python对象序列化 可用性:pickle至少1.4版本,cPickle 1.5版本以上 pickle模块实现了一种算法,将任意一个Python对象转化成一系列字节(byets)。此过程也调用了s...

    Sanchi 评论0 收藏0
  • python标准库学习之pickle模块

    摘要:利用标准库中的的模块可以将对象转换为一种可以传输或存储的格式。主要方法模块中有两个主要函数,它们是和。具体语法为返回一个字符串,而不是存入文件中。该方法用于反序列化,即将序列化的对象重新恢复成对象。除此之外,这两个模块的接口是几乎完全相同。 对象存在于程序运行时的内存中,当程序不再运行时或断电关机时,这些对象便不再存在。我现在想把对象保存下来,方便以后使用,这就是持久化技术。利用 py...

    宠来也 评论0 收藏0
  • Python基础之(十一)数据存储

    摘要:默认为或者说,是以格式保存对象如果设置为或者,则以压缩的二进制格式保存对象。但是,要小心坑试图增加一个坑就在这里当试图修改一个已有键的值时没有报错,但是并没有修改成功。要填平这个坑,需要这样做多一个参数没有坑了还用循环一下 pickle pickle是标准库中的一个模块,在Python 2中还有一个cpickle,两者的区别就是后者更快。所以,下面操作中,不管是用import pick...

    Songlcy 评论0 收藏0
  • Python列化安全问题

    摘要:反序列化安全问题一这一段时间使用做开发,使用了存储,阅读了源码,发现在存储到过程中,利用了模块进行序列化以及反序列化正好根据该样例学习一波反序列化相关的安全问题,不足之处请各位表哥指出。 Python 反序列化安全问题(一) 这一段时间使用flask做web开发,使用了redis存储session,阅读了flask_session源码,发现在存储session到redis过程中,利用了...

    Amos 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<