不使用cPickle创建数据持久存储的好处是什么?
我在考虑创建一个持久化存储,就像数据库管理系统(dbms)引擎一样。那我自己做一个二进制格式存储相比直接用cPickle来保存对象或者使用shelve模块,有什么好处呢?
7 个回答
请注意,并不是所有的对象都可以直接被“腌制”(也就是被序列化)——只有一些基本类型,或者那些已经定义了腌制协议的对象。
如果你使用自己的二进制格式,就可以存储任何类型的对象。
顺便提一下,Zope对象数据库(ZODB)就是采用这种方法,使用腌制格式来存储对象。你可能会对他们的实现方式感兴趣。
定义自己专属的二进制格式的一个原因是为了优化。pickle(还有使用pickle的shelve)是一个通用的序列化框架,它可以存储几乎所有的Python数据。虽然在很多情况下使用pickle很方便,但它需要花时间去检查所有对象并序列化它们的数据,而且这些数据本身是以一种通用且冗长的格式存储的。如果你存储的是一些特定的已知数据,自己制作的序列化工具可以更快、更简洁。
举个例子,序列化一个只有一个整数值的对象需要37个字节:
>>> import pickle >>> class Foo: pass... >>> foo = Foo() >>> foo.x = 3 >>> print repr(pickle.dumps(foo)) "(i__main__\nFoo\np0\n(dp1\nS'x'\np2\nI3\nsb."
在这些数据中包含了属性的名称和类型。为Foo(仅限Foo)定制的序列化工具可以省去这些信息,直接存储数字,这样可以节省时间和空间。
另一个使用自定义序列化框架的原因是你可以轻松地进行自定义验证和数据版本管理。如果你改变了对象类型,并且需要加载旧版本的数据,通过pickle可能会很麻烦。你自己的代码可以很容易地调整来处理旧的数据格式。
在实际操作中,我会先使用通用的cPickle模块,只有在性能分析显示真的很重要时才会考虑替换。维护一个单独的序列化框架需要花费相当多的精力。
最后,你可能会觉得这个资源有用:一些合成序列化器的基准测试。cPickle的速度相当快。
序列化就像一枚双面硬币。
一面,你可以很简单地把你的对象存储起来。只需要四行代码,你就能完成序列化。这样,你就能得到一个和原来一模一样的对象。
另一面,它可能会变成一个兼容性噩梦。如果你在代码中没有定义某个对象,或者它的定义和序列化时不一样,你就无法反序列化这个对象。这会大大限制你对代码的重构能力,或者对模块中的内容进行调整。
而且,并不是所有的东西都能被序列化。如果你对哪些东西可以序列化不够严格,而你的代码使用者又可以随意传入任何对象,迟早会有某个无法序列化的东西传入你的系统,那时候系统就会崩溃。
使用时一定要非常小心,这真的是一种快速但不太靠谱的方法。