资讯专栏INFORMATION COLUMN

Python 对象序列化——pickle and cPickle

Taonce / 1524人阅读

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

Python 对象序列化——pickle and cPickle

从这篇文章粗略翻译的pickle and cPickle

pickle模块可以实现任意的Python对象转换为一系列字节(即序列化对象)的算法。这些字节流可以 被传输或存储,接着也可以重构为一个和原先对象具有相同特征的新对象。

cPickle模块实现了同样的算法,但它是用c而不是python。因此,它比python实现的快上好几倍, 但是不允许使用者去继承Pickle。如果继承对于你的使用不是很重要,那么你大可以使用cPickle。

</>复制代码

  1. Woring: pickle的文档明确的表明它不提供安全保证。所以慎用pickle来作为内部进程通信或者数

  2. 据存储,也不要相信那些你不能验证安全性的数据。


Importing

通常优先试用 cPickle,只有当 cPickle 无法正常 import 的时候,采用 pickle 来替代。

</>复制代码

  1. try:
  2. import cPickle as pickle
  3. except:
  4. import pickle
Encoding and Decoding Data in Strings

第一个示例是将数据结构编码为字符串,然后输出到控制台。例子中数据结构完全由基本类型组成。pickle 可以编码任意类的实例,就像下面栗子中演示的那样:用 pickle.dumps() 来创建对象的字符串表示。

</>复制代码

  1. try:
  2. import cPickle as pickle
  3. except:
  4. import pickle
  5. import pprint
  6. data = [ { "a":"A", "b":2, "c":3.0 } ]
  7. print "DATA:",
  8. pprint.pprint(data)
  9. data_string = pickle.dumps(data)
  10. print "PICKLE:", data_string

pickle 默认试用 ASSCII 字符串来进行编码解码。也支持效率更高的二进制格式,但是下面的例子为了方便阅读,还是使用了 ASSCII 码。

</>复制代码

  1. $ python pickle_string.py
  2. DATA:[{"a": "A", "b": 2, "c": 3.0}]
  3. PICKLE: (lp1
  4. (dp2
  5. S"a"
  6. S"A"
  7. sS"c"
  8. F3
  9. sS"b"
  10. I2
  11. sa.

数据被序列化之后,你就可以将他写入文件、socket、pipe、etc.然后你可以读取文件并unpickle 这些数据来构造一个新的对象。

</>复制代码


  1. try:
  2. import cPickle as pickle
  3. except:
  4. import pickle
  5. import pprint
  6. data1 = [ { "a":"A", "b":2, "c":3.0 } ]
  7. print "BEFORE:",
  8. pprint.pprint(data1)
  9. data1_string = pickle.dumps(data1)
  10. data2 = pickle.loads(data1_string)
  11. print "AFTER:",
  12. pprint.pprint(data2)
  13. print "SAME?:", (data1 is data2)
  14. print "EQUAL?:", (data1 == data2)

如同例子中演示的那样,新的对象与之前的对象相等,但是并不是同一个对象。

</>复制代码


  1. $ python pickle_unpickle.py
  2. BEFORE:[{"a": "A", "b": 2, "c": 3.0}]
  3. AFTER:[{"a": "A", "b": 2, "c": 3.0}]
  4. SAME?: False
  5. EQUAL?: True
Working with Streams

除了 dumps() 跟 loads(), pickle 还有其他比较方便的方法来操作类文件流。可以同时写入多个对象到一个 stream 中,然后对象数量与大小的时候从 stream 读取他们。

</>复制代码


  1. try:
  2. import cPickle as pickle
  3. except:
  4. import pickle
  5. import pprint
  6. from StringIO import StringIO
  7. class SimpleObject(object):
  8. def __init__(self, name):
  9. self.name = name
  10. l = list(name)
  11. l.reverse()
  12. self.name_backwards = "".join(l)
  13. return
  14. data = []
  15. data.append(SimpleObject("pickle"))
  16. data.append(SimpleObject("cPickle"))
  17. data.append(SimpleObject("last"))
  18. # Simulate a file with StringIO
  19. out_s = StringIO()
  20. # Write to the stream
  21. for o in data:
  22. print "WRITING: %s (%s)" % (o.name, o.name_backwards)
  23. pickle.dump(o, out_s)
  24. out_s.flush()
  25. # Set up a read-able stream
  26. in_s = StringIO(out_s.getvalue())
  27. # Read the data
  28. while True:
  29. try:
  30. o = pickle.load(in_s)
  31. except EOFError:
  32. break
  33. else:
  34. print "READ: %s (%s)" % (o.name, o.name_backwards)

上面例子中使用了 StringIO 缓冲区来模拟streams,这样我们在建立可读流的时候可以玩一些技巧。一些接单的数据库格式也可以使用 pickles 来存储数据,当然,如果试用 shelve 来存储会更加简单。

</>复制代码

  1. $ python pickle_stream.py
  2. WRITING: pickle (elkcip)
  3. WRITING: cPickle (elkciPc)
  4. WRITING: last (tsal)
  5. READ: pickle (elkcip)
  6. READ: cPickle (elkciPc)
  7. READ: last (tsal)

除了存储数据,pickles 用来做内部通信的机会也很多。举个例子:用 os.fork() 和 os.pipe() 可以建立一个工作进程,然后这个进程会从管道中读取数据并把结果传递给另外一个管道。因为这些代码是用来管理worker pool 跟 发送任务跟接受任务的,没有什么特殊的内容,所以这些核心代码可以被拿来重复利用。如果你在试用 pipe 或者 sockets,那么在 dumping完对象之后,不要忘记刷新它们并通过其间的连接将数据推送到另外一个进程。如果你不想自己写 worker pool manager 的话,可以看一下multiprocessing

Problems Reconstructing Objects

需要注意的是,在序列化实例的时候,我们只是对于数据来进行序列化,而无法对类的定义进行序列化。
下面的栗子:

</>复制代码

  1. try:
  2. import cPickle as pickle
  3. except:
  4. import pickle
  5. import sys
  6. class SimpleObject(object):
  7. def __init__(self, name):
  8. self.name = name
  9. l = list(name)
  10. l.reverse()
  11. self.name_backwards = "".join(l)
  12. return
  13. if __name__ == "__main__":
  14. data = []
  15. data.append(SimpleObject("pickle"))
  16. data.append(SimpleObject("cPickle"))
  17. data.append(SimpleObject("last"))
  18. try:
  19. filename = sys.argv[1]
  20. except IndexError:
  21. raise RuntimeError("Please specify a filename as an argument to %s" % sys.argv[0])
  22. out_s = open(filename, "wb")
  23. try:
  24. # Write to the stream
  25. for o in data:
  26. print "WRITING: %s (%s)" % (o.name, o.name_backwards)
  27. pickle.dump(o, out_s)
  28. finally:
  29. out_s.close()

运行的时候,这个脚本会以命令行中给出的参数创建一个文件。

</>复制代码

  1. $ python pickle_dump_to_file_1.py test.dat
  2. WRITING: pickle (elkcip)
  3. WRITING: cPickle (elkciPc)
  4. WRITING: last (tsal)

下面是一个会报错的栗子:

</>复制代码

  1. try:
  2. import cPickle as pickle
  3. except:
  4. import pickle
  5. import pprint
  6. from StringIO import StringIO
  7. import sys
  8. try:
  9. filename = sys.argv[1]
  10. except IndexError:
  11. raise RuntimeError("Please specify a filename as an argument to %s" % sys.argv[0])
  12. in_s = open(filename, "rb")
  13. try:
  14. # Read the data
  15. while True:
  16. try:
  17. o = pickle.load(in_s)
  18. except EOFError:
  19. break
  20. else:
  21. print "READ: %s (%s)" % (o.name, o.name_backwards)
  22. finally:
  23. in_s.close()

这个报错是因为没有SimpleObject类。

</>复制代码

  1. $ python pickle_load_from_file_1.py test.dat
  2. Traceback (most recent call last):
  3. File "pickle_load_from_file_1.py", line 52, in
  4. o = pickle.load(in_s)
  5. AttributeError: "module" object has no attribute "SimpleObject"

我们通过从原来的脚本中import SimpleObject来修正上面的错误。
在上面文件中的增加下面一行:

</>复制代码

  1. from pickle_dump_to_file_1 import SimpleObject

</>复制代码

  1. $ python pickle_load_from_file_2.py test.dat
  2. READ: pickle (elkcip)
  3. READ: cPickle (elkciPc)
  4. READ: last (tsal)

sockets, file handles, database connections ...这些数据类型是无法被序列化的,我们在处理类似数据类型的时候不得不特殊处理。而利用pickle protocol 则可以控制序列化的细节。

</>复制代码

  1. class Data(object):
  2. def __init__(self, x, y):
  3. self._x = x
  4. self._y = y
  5. def __getstate__(self):
  6. d = self.__dict__.copy()
  7. del d["_y"]
  8. return d
  9. def __setstate__(self, state):
  10. self.__dict__.update(state)
  11. d = Data(10, 20)
  12. s = cPickle.dumps(d, 2)
  13. d2 = cPickle.loads(s)
  14. ##d2.__dict__
  15. ##{"_x": 10}

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

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

相关文章

  • pickle和cPicklePython对象列化(上)

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

    Sanchi 评论0 收藏0
  • pickle和cPicklePython对象列化(下)

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

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

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

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

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

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

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

    Songlcy 评论0 收藏0

发表评论

0条评论

Taonce

|高级讲师

TA的文章

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