7.1 pickle

pickle是标准库中的一个模块,还有跟它完全一样的叫作cpickle,两者的区别就是后者更快(似乎已经是一个规律了,凡是某个模块前面有c,就意味着它是用c语言重写了,也意味着速度更快些)。所以,在下面的操作中,不管是用import pickle,还是用import cpickle as pickle,在功能上都是一样的。

  1. >>> import pickle
  2. >>> integers = [1, 2, 3, 4, 5]
  3. >>> f = open("22901.dat", "wb")
  4. >>> pickle.dump(integers, f)
  5. >>> f.close()

用pickle.dump(integers,f)将数据integers保存到文件22901.dat中。如果你要打开这个文件看里面的内容,可能会有点失望,但是,它对计算机是友好的。这个步骤可以称之为将对象序列化。用到的方法是:pickle.dump(obj,file[,protocol])。

  • obj:序列化对象,在上面的例子中是一个列表,它是基本类型,也可以序列化自己定义的类型。
  • file:要写入的文件。可以更广泛地理解为拥有write()方法的对象,并且能接受字符串为参数,所以,它还可以是一个StringIO对象,或者其他自定义满足条件的对象。
  • protocol:可选项。默认为False(或者说0),以ASCII格式保存对象;如果设置为1或者True,则以压缩的二进制格式保存对象。换一种数据格式,并且做对比:
  1. >>> import pickle
  2. >>> d = {}
  3. >>> integers = range(9999)
  4. >>> d["i"] = integers #下面将这个dict格式的对象存入文件
  5.  
  6. >>> f = open("22902.dat", "wb")
  7. >>> pickle.dump(d, f) #文件中以ascii格式保存数据
  8. >>> f.close()
  9.  
  10. >>> f = open("22903.dat", "wb")
  11. >>> pickle.dump(d, f, True) #文件中以二进制格式保存数据
  12. >>> f.close()
  13.  
  14. >>> import os
  15. >>> s1 = os.stat("22902.dat").st_size #得到两个文件的大小
  16. >>> s2 = os.stat("22903.dat").st_size
  17.  
  18. >>> print "%d, %d, %.2f%%" % (s1, s2, (s2+0.0)/s1*100)
  19. 68903, 29774, 43.21%

比较结果发现,以二进制方式保存的文件比以ASCII格式保存的文件小很多,前者约是后者的43%。

所以,在序列化的时候,特别是面对较大对象时,建议将dump()的参数True设置上,虽然现在存储设备的价格便宜,但是能省的还是省点比较好。

存入文件,还有另外一个目标,就是要读出来,也称之为反序列化。

  1. >>> integers = pickle.load(open("22901.dat", "rb"))
  2. >>> print integers
  3. [1, 2, 3, 4, 5]

再看看被以二进制方式存入的那个文件:

  1. >>> f = open("22903.dat", "rb")
  2. >>> d = pickle.load(f)
  3. >>> print d
  4. {'i': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, .... #省略后面的数字}
  5. >>> f.close()

如果是自己定义的数据类型,是否可以用上述方式存入文件并读出来呢?看下面的例子:

  1. >>> import cPickle as pickle #cPickle更快
  2. >>> import StringIO #标准库中的一个模块,跟file功能类似,
  3. #只不过是在内存中操作“文件”
  4. >>> class Book(object): #自定义一种类型
  5. ... def __init__(self,name):
  6. ... self.name = name
  7. ... def my_book(self):
  8. ... print "my book is: ", self.name
  9. ...
  10.  
  11. >>> pybook = Book("<from beginner to master>")
  12. >>> pybook.my_book()
  13. my book is: <from beginner to master>
  14.  
  15. >>> file = StringIO.StringIO()
  16. >>> pickle.dump(pybook, file, 1)
  17. >>> print file.getvalue() #查看“文件”内容,注意下面不是乱码
  18. ccopy_reg
  19. _reconstructor
  20. q?(c__main__
  21. Book
  22. q?c__builtin__
  23. object
  24. q?NtRq?}qU?nameq?U?<from beginner to master>sb.
  25.  
  26. >>> pickle.dump(pybook, file) #换一种方式,再看内容,可以比较一下
  27. >>> print file.getvalue() #视觉上两者就有很大差异
  28. ccopy_reg
  29. _reconstructor
  30. q?(c__main__
  31. Book
  32. q?c__builtin__
  33. object
  34. q?NtRq?}qU?nameq?U?<from beginner to master>sb.ccopy_reg
  35. _reconstructor
  36. p1
  37. (c__main__
  38. Book
  39. p2
  40. c__builtin__
  41. object
  42. p3
  43. NtRp4
  44. (dp5
  45. S'name'
  46. p6
  47. S'<from beginner to master>'
  48. p7
  49. sb.

如果从文件中读出来:

  1. >>> file.seek(0) #找到对应类型
  2. >>> pybook2 = pickle.load(file)
  3. >>> pybook2.my_book()
  4. my book is: <from beginner to master>
  5. >>> file.close()