为什么加载pickle对象比加载文件耗时更长?
我注意到,用pickle加载一个包含5000个对象的字典需要很长时间(几分钟),而用json加载一个包含5000个实体的文件却只需要很短的时间(几秒钟)。我知道一般来说,处理对象会有一些额外的开销——在面向对象编程中,这种跟踪对象的开销是使用它们的便利性的一部分。但为什么加载一个被pickle处理过的对象会花这么长时间呢?在背后发生了什么?将一个对象序列化和仅仅把它的数据写入文件相比,成本有什么不同?pickle在恢复对象时是否会把它放回内存中的相同位置,或者说可能会把其他对象挪开?如果序列化的速度更慢(至少pickle是这样),那它的好处是什么呢?
2 个回答
有一些关于特定对象序列化速度的比较,主要是比较JSON、pickle和cPickle。每种格式对每个对象的速度都不一样。通常来说,JSON的速度比pickle快,而且你常常会听到不要使用pickle,因为它不安全。安全问题的原因,以及一些速度上的差距,是因为pickle并没有序列化很多数据——它实际上序列化了一些数据和一堆指令,这些指令用于组装Python对象。如果你看过dis
模块,就会看到pickle为每个对象使用的指令类型。cPickle和json一样,不是纯Python,而是利用了优化过的C语言,所以通常会更快。
一般来说,pickle序列化后的数据占用的空间比直接存储对象要小,然而,有些指令集可能会比较大。JSON通常占用的空间更小,而且人类可读……不过,由于JSON把所有东西都存储为人类可读的字符串,它不能像pickle和cPickle那样序列化那么多不同类型的对象。所以,选择JSON的权衡在于“安全性”(或者说不灵活,取决于你的观点)和人类可读性,而pickle则可以序列化更多种类的对象。
另一个选择pickle(而不是json)的好理由是,你可以很容易地扩展pickle,这意味着你可以注册一个新的方法来序列化pickle不知道怎么处理的对象。Python提供了几种方法来做到这一点……比如__getstate__
和__setstate__
,还有copy_reg
方法。通过使用这些方法,你会发现人们已经扩展了pickle,可以序列化大多数Python对象,比如dill
。
需要注意的是,pickle并不会把对象恢复到相同的内存位置。但是,它确实会把对象恢复到与被序列化时相同的状态(通常是这样的)。如果你想了解人们为什么使用pickle,可以看看这里:
http://nbviewer.ipython.org/gist/minrk/5241793
http://matthewrocklin.com/blog/work/2013/12/05/Parallelism-and-Serialization/
假设你正在使用Python 2.7的标准库中的pickle
和json
模块...
- Python 2.7默认使用的是纯Python实现的
pickle
模块,虽然也有一个更快的C语言实现可以使用。http://docs.python.org/2/library/pickle.html - 相反,Python 2.7默认使用的是经过优化的C语言实现的
json
模块:http://docs.python.org/dev/whatsnew/2.7.html
所以你其实是在比较一个纯Python的反序列化工具和一个经过优化的C语言反序列化工具。这样的比较不太公平,即使它们的序列化格式是一样的。