Python cPickle模块(转)
添加时间:2013-8-5 点击量:
持久性就是指对峙对象,甚至在多次履行同一法度之间也对峙对象。经由过程本文,您会对 Python对象的各类持久性机制(从关系数据库到 Python 的 pickle以及其它机制)有一个总体熟悉。别的,还会让您更深一步地懂得Python 的对象序列化才能。
什么是持久性?
持久性的根蒂根基思惟很简单。假定有一个 Python 法度,它可能是一个经管日常待就事项的法度,您在多次履行这个法度之间可以保存应用法度对象(待就事项)。换句话说,您将对象存储在磁盘上,便于今后检索。这就是持久性。要达到这个目标,有几种办法,每一种办法都有其优毛病。
例如,可以将对象数据存储在某种格局的文本文件中,譬如 CSV 文件。或者可以用关系数据库,譬如 Gadfly、MySQL、PostgreSQL 或者 DB2。这些文件格局和数据库都很是优良,对于所有这些存储机制,Python 都有结实的接口。
这些存储机制都有一个共同点:存储的数据是自力于对这些数据进行操纵的对象和法度。如许做的益处是,数据可以作为共享的资料,供其它应用法度应用。毛病是,用这种体式格式,可以容许其它法度接见对象的数据,这违背了面向对象的封装性原则 — 即对象的数据只能经由过程这个对象自身的公共(public)接口来接见。
别的,对于某些应用法度,关系数据库办法可能不是很幻想。尤其是,关系数据库不睬解对象。相反,关系数据库会强行应用本身的类型体系和关系数据模型(表),每张表包含一组元组(行),每行包含具有固定命目标静态类型字段(列)。若是应用法度的对象模型不克不及够便利地转换到关系模型,那么在将对象映射到元组以及将元组映射回对象方面,会碰着必然难度。这种艰苦常被称为阻碍性不匹配(impedence-mismatch)题目。
对象持久性
若是透明地存储 Python 对象,而不丧失其身份和类型等信息,则须要某种情势的对象序列化:它是一个将随便率性错杂的对象转成对象的文本或二进制默示的过程。同样,必须可以或许将对象经过序列化后的情势恢复到原有的对象。在 Python 中,这种序列化过程称为 pickle,可以将对象 pickle 成字符串、磁盘上的文件或者任何类似于文件的对象,也可以将这些字符串、文件或任何类似于文件的对象 unpickle 成本来的对象。我们将在本文后面具体评论辩论 pickle。
假定您喜好将任何事物都保存成对象,并且避免将对象转换成某种基于非对象存储的开销;那么 pickle 文件可以供给这些益处,但有时可能须要比这种简单的 pickle 文件更结实以及更具有可伸缩性的事物。例如,只用 pickle 不克不及解决定名和查找 pickle 文件如许的题目,别的,它也不克不及支撑并发地接见持久性对象。若是须要这些方面的功能,则要乞助类似于 ZODB(针对 Python 的 Z 对象数据库)这类数据库。ZODB 是一个结实的、多用户的和面向对象的数据库体系,它可以或许存储和经管随便率性错杂的 Python 对象,并支撑事务操纵和并发把握。(请参阅 参考材料,以 ZODB。)令人足够感爱好的是,甚至 ZODB 也依附 Python 的本机序列化才能,并且要有效地应用 ZODB,必须充沛懂得 pickle。
另一种令人感爱好的解决持久性题目的办法是 Prevayler,它最初是用 Java 实现的(有关 Prevaylor 方面的developerWorks 文章,请参阅 参考材料)。比来,一群 Python 法度员将 Prevayler 移植到了 Python 上,另起名为 PyPerSyst,由 SourceForge 托管(有关至 PyPerSyst 项目标链接,请参阅 参考材料)。Prevayler/PyPerSyst 概念也是建树在 Java 和 Python 说话的本机序列化才能之上。PyPerSyst 将全部对象体系保存在内存中,并经由过程不时地将体系快照 pickle 到磁盘以及保护一个号令日记(经由过程此日记可以从头应用新的快照)来供给灾害恢复。所以,尽管应用 PyPerSyst 的应用法度受到可用内存的限制,但益处是本机对象体系可以完全装入到内存中,因而速度极快,并且实现起来要比如 ZODB 如许的数据库简单,ZODB 容许对象的数量比同时在能内存中所对峙的对象要多。
既然我们已经扼要评论辩论了存储持久对象的各类办法,那么如今该具体商量 pickle 过程了。固然我们首要感爱好的是摸索以各类体式格式来保存 Python 对象,而不必将其转换成某种其它格局,但我们仍然还有一些须要存眷的处所,譬如:如何有效地 pickle 和 unpickle 简单对象以及错杂对象,包含定制类的实例;如何保护对象的引用,包含轮回引用和递归引用;以及如何处理惩罚类定义产生的变更,从而应用以前经过 pickle 的实例时不会产生题目。我们将在随后关于 Python 的 pickle 才能商量中涉及所有这些题目。
一些经过 pickle 的 Python
pickle 模块及其同类模块 cPickle 向 Python 供给了 pickle 支撑。后者是用 C 编码的,它具有更好的机能,对于大多半应用法度,推荐应用该模块。我们将持续评论辩论 pickle ,但本文的示例实际是哄骗了 cPickle 。因为此中大多半示例要用 Python shell 来显示,所以先显现一下如何导入 cPickle ,并可以作为 pickle 来引用它:
>>> import cPickle as pickle
如今已经导入了该模块,接下来让我们看一下 pickle 接口。 pickle 模块供给了以下函数对: dumps(object) 返回一个字符串,它包含一个 pickle 格局的对象; loads(string) 返回包含在 pickle 字符串中的对象; dump(object, file) 将对象写到文件,这个文件可所以实际的物理文件,但也可所以任何类似于文件的对象,这个对象具有 write() 办法,可以接管单个的字符串参数; load(file) 返回包含在 pickle 文件中的对象。
缺省景象下, dumps() 和 dump() 应用可打印的 ASCII 默示来创建 pickle。两者都有一个 final 参数(可选),若是为 True ,则该参数指定用更快以及更小的二进制默示来创建 pickle。 loads() 和 load() 函数主动检测 pickle 是二进制格局还是文本格局。
清单 1 显示了一个交互式会话,这里应用了刚才所描述的 dumps() 和 loads() 函数:
清单 1. dumps() 和 loads() 的演示:
01.>>> import cPickle as pickle
02.>>> t1 = (this is a string, 42, [1, 2, 3], None)
03.>>> t1
04.(this is a string, 42, [1, 2, 3], None)
05.>>> p1 = pickle.dumps(t1)
06.>>> p1
07.(Sthis is a string/nI42/n(lp1/nI1/naI2/naI3/naNtp2/n.
08.>>> print p1
09.(Sthis is a string
10.I42
11.(lp1
12.I1
13.aI2
14.aI3
15.aNtp2
16..
17.>>> t2 = pickle.loads(p1)
18.>>> t2
19.(this is a string, 42, [1, 2, 3], None)
20.>>> p2 = pickle.dumps(t1, True)
21.>>> p2
22.(U/x10this is a stringK]q/x01(K/x01K/x02K/x03eNtq/x02.
23.>>> t3 = pickle.loads(p2)
24.>>> t3
25.(this is a string, 42, [1, 2, 3], None)
注:该文本 pickle 格局很简单,这里就不说了然。事实上,在 pickle 模块中记录了所有应用的商定。我们还应当指出,在我们的示例中应用的都是简单对象,是以应用二进制 pickle 格局不会在节俭空间上显示出太大的效力。然而,在实际应用错杂对象的体系中,您会看到,应用二进制格局可以在大小和速度方面带来明显的改进。
接下来,我们看一些示例,这些示例用到了 dump() 和 load() ,它们应用文件和类似文件的对象。这些函数的操纵很是类似于我们刚才所看到的 dumps() 和 loads() ,差别在于它们还有另一种才能 — dump() 函数能一个接着一个地将几个对象转储到同一个文件。随后调用 load() 来以同样的次序检索这些对象。清单 2 显示了这种才能的实际应用:
清单 2. dump() 和 load() 示例:
01.>>> a1 = apple
02.>>> b1 = {1: One, 2: Two, 3: Three}
03.>>> c1 = [fee, fie, foe, fum]
04.>>> f1 = file(temp.pkl, wb)
05.>>> pickle.dump(a1, f1, True)
06.>>> pickle.dump(b1, f1, True)
07.>>> pickle.dump(c1, f1, True)
08.>>> f1.close()
09.>>> f2 = file(temp.pkl, rb)
10.>>> a2 = pickle.load(f2)
11.>>> a2
12.apple
13.>>> b2 = pickle.load(f2)
14.>>> b2
15.{1: One, 2: Two, 3: Three}
16.>>> c2 = pickle.load(f2)
17.>>> c2
18.[fee, fie, foe, fum]
19.>>> f2.close()
Pickle 的威力
到今朝为止,我们讲述了关于 pickle 方面的根蒂根基常识。在这一节,将评论辩论一些高等题目,当您开端 pickle 错杂对象时,会碰到这些题目,此中包含定制类的实例。荣幸的是,Python 可以很轻易地处理惩罚这种景象。
可移植性
从空间和时候上说,Pickle 是可移植的。换句话说,pickle 文件格局自力于机械的体系布局,这意味着,例如,可以在 Linux 下创建一个 pickle,然后将它发送到在 Windows 或 Mac OS 下运行的 Python 法度。并且,当进级到更新版本的 Python 时,不必愁闷可能要放弃已有的 pickle。Python 开辟人员已经包管 pickle 格局将可以向后兼容 Python 各个版本。事实上,在 pickle 模块中供给了有关今朝以及所支撑的格局方面的具体信息.
清单 3. 检索所支撑的格局:
01.>>> pickle.format_version
02.1.3
03.>>> pickle.compatible_formats
04.[1.0, 1.1, 1.2]
多个引用,同一对象
在 Python 中,变量是对象的引用。同时,也可以用多个变量引用同一个对象。经证实,Python 在用经过 pickle 的对象保护这种行动方面涓滴没有艰苦,如清单 4 所示:
清单 4. 对象引用的保护:
01.>>> a = [1, 2, 3]
02.>>> b = a
03.>>> a
04.[1, 2, 3]
05.>>> b
06.[1, 2, 3]
07.>>> a.append(4)
08.>>> a
09.[1, 2, 3, 4]
10.>>> b
11.[1, 2, 3, 4]
12.>>> c = pickle.dumps((a, b))
13.>>> d, e = pickle.loads(c)
14.>>> d
15.[1, 2, 3, 4]
16.>>> e
17.[1, 2, 3, 4]
18.>>> d.append(5)
19.>>> d
20.[1, 2, 3, 4, 5]
21.>>> e
22.[1, 2, 3, 4, 5]
轮回引用和递归引用
可以将刚才演示过的对象引用支撑扩大到 轮回引用(两个对象各自包含对对方的引用)和 递归引用(一个对象包含对其自身的引用)。下面两个清单侧重显示这种才能。我们先看一下递归引用:
清单 5. 递归引用:
01.>>> l = [1, 2, 3]
02.>>> l.append(l)
03.>>> l
04.[1, 2, 3, [...]]
05.>>> l[3]
06.[1, 2, 3, [...]]
07.>>> l[3][3]
08.[1, 2, 3, [...]]
09.>>> p = pickle.dumps(l)
10.>>> l2 = pickle.loads(p)
11.>>> l2
12.[1, 2, 3, [...]]
13.>>> l2[3]
14.[1, 2, 3, [...]]
15.>>> l2[3][3]
16.[1, 2, 3, [...]]
如今,看一个轮回引用的示例:
清单 6. 轮回引用:
01.>>> a = [1, 2]
02.>>> b = [3, 4]
03.>>> a.append(b)
04.>>> a
05.[1, 2, [3, 4]]
06.>>> b.append(a)
07.>>> a
08.[1, 2, [3, 4, [...]]]
09.>>> b
10.[3, 4, [1, 2, [...]]]
11.>>> a[2]
12.[3, 4, [1, 2, [...]]]
13.>>> b[2]
14.[1, 2, [3, 4, [...]]]
15.>>> a[2] is b
16.1
17.>>> b[2] is a
18.1
19.>>> f = file(temp.pkl, w)
20.>>> pickle.dump((a, b), f)
21.>>> f.close()
22.>>> f = file(temp.pkl, r)
23.>>> c, d = pickle.load(f)
24.>>> f.close()
25.>>> c
26.[1, 2, [3, 4, [...]]]
27.>>> d
28.[3, 4, [1, 2, [...]]]
29.>>> c[2]
30.[3, 4, [1, 2, [...]]]
31.>>> d[2]
32.[1, 2, [3, 4, [...]]]
33.>>> c[2] is d
34.1
35.>>> d[2] is c
36.1
重视,若是分别 pickle 每个对象,而不是在一个元组中一路 pickle 所有对象,会获得略微不合(但很首要)的成果,如清单 7 所示:
清单 7. 分别 pickle vs. 在一个元组中一路 pickle:
01.>>> f = file(temp.pkl, w)
02.>>> pickle.dump(a, f)
03.>>> pickle.dump(b, f)
04.>>> f.close()
05.>>> f = file(temp.pkl, r)
06.>>> c = pickle.load(f)
07.>>> d = pickle.load(f)
08.>>> f.close()
09.>>> c
10.[1, 2, [3, 4, [...]]]
11.>>> d
12.[3, 4, [1, 2, [...]]]
13.>>> c[2]
14.[3, 4, [1, 2, [...]]]
15.>>> d[2]
16.[1, 2, [3, 4, [...]]]
17.>>> c[2] is d
18.0
19.>>> d[2] is c
20.0
相等,但并不老是雷同
正如在上一个示例所暗示的,只有在这些对象引用内存中同一个对象时,它们才是雷同的。在 pickle 景象中,每个对象被恢复到一个与本来对象相等的对象,但不是同一个对象。换句话说,每个 pickle 都是本来对象的一个副本:
清单 8. 作为本来对象副本的被恢复的对象:
01.>>> j = [1, 2, 3]
02.>>> k = j
03.>>> k is j
04.1
05.>>> x = pickle.dumps(k)
06.>>> y = pickle.loads(x)
07.>>> y
08.[1, 2, 3]
09.>>> y == k
10.1
11.>>> y is k
12.0
13.>>> y is j
14.0
15.>>> k is j
16.1
同时,我们看到 Python 可以或许保护对象之间的引用,这些对象是作为一个单位进行 pickle 的。然而,我们还看到分别调用 dump() 会使 Python 无法保护对在该单位外部进行 pickle 的对象的引用。相反,Python 复制了被引用对象,并将副本和被 pickle 的对象存储在一路。对于 pickle 和恢复单个对象层次布局的应用法度,这是没有题目的。但要意识到还有其它景象。
值得指出的是,有一个选项确切容许分别 pickle 对象,并保护彼此之间的引用,只要这些对象都是 pickle 到同一文件即可。 pickle 和 cPickle 模块供给了一个 Pickler (与此相对应是 Unpickler ),它可以或许跟踪已经被 pickle 的对象。经由过程应用这个 Pickler ,将会经由过程引用而不是经由过程值来 pickle 共享和轮回引用:
清单 9. 保护分别 pickle 的对象间的引用:
01.>>> f = file(temp.pkl, w)
02.>>> pickler = pickle.Pickler(f)
03.>>> pickler.dump(a)
04.<cPickle.Pickler object at 0 x89b0bb8>
05.>>> pickler.dump(b)
06.<cPickle.Pickler object at 0 x89b0bb8>
07.>>> f.close()
08.>>> f = file(temp.pkl, r)
09.>>> unpickler = pickle.Unpickler(f)
10.>>> c = unpickler.load()
11.>>> d = unpickler.load()
12.>>> c[2]
13.[3, 4, [1, 2, [...]]]
14.>>> d[2]
15.[1, 2, [3, 4, [...]]]
16.>>> c[2] is d
17.1
18.>>> d[2] is c
19.1
不成 pickle 的对象
一些对象类型是不成 pickle 的。例如,Python 不克不及 pickle 文件对象(或者任何带有对文件对象引用的对象),因为 Python 在 unpickle 时不克不及包管它可以重建该文件的状况(另一个示例斗劲难懂,在这类文章中不值得提出来)。试图 pickle 文件对象会导致以下错误:
清单 10. 试图 pickle 文件对象的成果:
01.>>> f = file(temp.pkl, w)
02.>>> p = pickle.dumps(f)
03.Traceback (most recent call last):
04. File <input>, line 1, in ?
05. File /usr/lib/python2.2/copy_reg.py, line 57, in _reduce
06. raise TypeError, cant pickle %s objects % base.__name__
07.TypeError: cant pickle file objects
类实例
与 pickle 简单对象类型比拟,pickle 类实例要多加留心。这首要因为 Python 会 pickle 实例数据(凡是是 _dict_ 属性)和类的名称,而不会 pickle 类的代码。当 Python unpickle 类的实例时,它会试图应用在 pickle 该实例时的确切的类名称和模块名称(包含任何包的路径前缀)导入包含该类定义的模块。别的要重视,类定义必须呈如今模块的最顶层,这意味着它们不克不及是嵌套的类(在其它类或函数中定义的类)。
当 unpickle 类的实例时,凡是不会再调用它们的 _init_() 办法。相反,Python 创建一个通用类实例,并应用已进行过 pickle 的实例属性,同时设置该实例的 _class_ 属性,使其指向本来的类。
对 Python 2.2 中引入的新型类进行 unpickle 的机制与本来的略有不合。固然处理惩罚的成果实际上与对旧型类处理惩罚的成果雷同,但 Python 应用 copy_reg 模块的 _reconstructor() 函数来恢复新型类的实例。
若是对新型或旧型类的实例批改缺省的 pickle 行动,则可以定义特别的类的办法 _getstate_() 和 _setstate_() ,在保存和恢复类实例的状况信息时代,Python 会调用这些办法。在以下几节中,我们会看到一些示例哄骗了这些特别的办法。
如今,我们看一个简单的类实例。起首,创建一个 persist.py 的 Python 模块,它包含以下新型类的定义:
清单 11. 新型类的定义:
01.class Foo(object):
02. def __init__(self, value):
03. self.value = value
如今可以 pickle Foo 实例,并看一下它的默示:
清单 12. pickle Foo 实例:
01.>>> import cPickle as pickle
02.>>> Orbtech.examples.persist import Foo
03.>>> foo = Foo(What is a Foo?)
04.>>> p = pickle.dumps(foo)
05.>>> print p
06.ccopy_reg
07._reconstructor
08.p1
09.(cOrbtech.examples.persist
10.Foo
11.p2
12.c__builtin__
13.object
14.p3
15.NtRp4
16.(dp5
17.Svalue
18.p6
19.SWhat is a Foo?
20.sb.
可以看到这个类的名称 Foo 和全限制的模块名称 Orbtech.examples.persist 都存储在 pickle 中。若是将这个实例 pickle 成一个文件,稍后再 unpickle 它或在另一台机械上 unpickle,则 Python 会试图导入 Orbtech.examples.persist 模块,若是不克不及导入,则会抛出异常。若是重定名该类和该模块或者将该模块移到另一个目次,则也会产生类似的错误。
这里有一个 Python 发失足误消息的示例,重定名 Foo 类,然后试图装入进步前辈步行过 pickle 的 Foo 实例时会产生该错误:
清单 13. 试图装入一个被重定名的 Foo 类的经过 pickle 的实例:
01.>>> import cPickle as pickle
02.>>> f = file(temp.pkl, r)
03.>>> foo = pickle.load(f)
04.Traceback (most recent call last):
05. File <input>, line 1, in ?
06.AttributeError: module object has no attribute Foo
在重定名 persist.py 模块之后,也会产生类似的错误:
清单 14. 试图装入一个被重定名的 persist.py 模块的经过 pickle 的实例:
01.>>> import cPickle as pickle
02.>>> f = file(temp.pkl, r)
03.>>> foo = pickle.load(f)
04.Traceback (most recent call last):
05. File <input>, line 1, in ?
06.ImportError: No module named persist
我们会鄙人面 模式改进这一节供给一些技巧来经管这类更改,而不会破损现有的 pickle。
特别的状况办法
前面提到对一些对象类型(譬如,文件对象)不克不及进行 pickle。处理惩罚这种不克不及 pickle 的对象的实例属性时可以应用特别的办法( _getstate_() 和 _setstate_() )来批改类实例的状况。这里有一个 Foo 类的示例,我们已经对它进行了批改以处理惩罚文件对象属性:
清单 15. 处理惩罚不克不及 pickle 的实例属性:
01.class Foo(object):
02. def __init__(self, value, filename):
03. self.value = value
04. self.logfile = file(filename, w)
05. def __getstate__(self):
06. Return state values to be pickled.
07. f = self.logfile
08. return (self.value, f.name, f.tell())
09. def __setstate__(self, state):
10. Restore state the unpickled state values.
11. self.value, name, position = state
12. f = file(name, w)
13. f.seek(position)
14. self.logfile = f
pickle Foo 的实例时,Python 将只 pickle 当它调用该实例的 _getstate_() 办法时返回给它的值。类似的,在 unpickle 时,Python 将供给经过 unpickle 的值作为参数传递给实例的 _setstate_() 办法。在 _setstate_() 办法内,可以按照经过 pickle 的名称和地位信息来重建文件对象,并将该文件对象分派给这个实例的 logfile 属性。
模式改进
跟着时候的推移,您会发明本身必必要更改类的定义。若是已经对某个类实例进行了 pickle,而如今又须要更改这个类,则您可能要检索和更新那些实例,以便它们能在新的类定义下持续正常工作。而我们已经看到在对类或模块进行某些更改时,会呈现一些错误。荣幸的是,pickle 和 unpickle 过程供给了一些 hook,我们可以用它们来支撑这种模式改进的须要。
在这一节,我们将商量一些办法来猜测常见题目以及如何解决这些题目。因为不克不及 pickle 类实例代码,是以可以添加、更改和除去办法,而不会影响现有的经过 pickle 的实例。出于同样的原因,可以不必愁闷类的属性。您必须确保包含类定义的代码模块在 unpickle 景象中可用。同时还必须为这些可能导致 unpickle 题目的更改做好规划,这些更改包含:更改类名、添加或除去实例的属性以及改变类定义模块的名称或地位。
类名的更改
要更改类名,而不破损先前经过 pickle 的实例,请遵守以下步调。起首,确保本来的类的定义没有被更改,以便在 unpickle 现有实例时可以找到它。不要更改本来的名称,而是在与本来类定义地点的同一个模块中,创建该类定义的一个副本,同时给它一个新的类名。然后应用实际的新类名来调换 NewClassName ,将以下办法添加到本来类的定义中:
清单 16. 更改类名:添加到本来类定义的办法:
01.def __setstate__(self, state):
02. self.__dict__.(state)
03. self.__class__ = NewClassName
当 unpickle 现有实例时,Python 将查找本来类的定义,并调用实例的 _setstate_() 办法,同时将给新的类定义从头分派该实例的 _class_ 属性。一旦断定所有现有的实例都已经 unpickle、更新和从头 pickle 后,可以从源代码模块中除去旧的类定义。
属性的添加和删除
这些特别的状况办法 _getstate_() 和 _setstate_() 再一次使我们能把握每个实例的状况,并使我们有机会处理惩罚实例属性中的更改。让我们看一个简单的类的定义,我们将向其添加和除去一些属性。这是是最初的定义:
清单 17. 最初的类定义:
01.class Person(object):
02. def __init__(self, firstname, lastname):
03. self.firstname = firstname
04. self.lastname = lastname
假定已经创建并 pickle 了 Person 的实例,如今我们决意真的只想存储一个名称属性,而不是分别存储姓和名。这里有一种体式格式可以更改类的定义,它将先前经过 pickle 的实例迁徙到新的定义:
清单 18. 新的类定义:
01.class Person(object):
02. def __init__(self, fullname):
03. self.fullname = fullname
04. def __setstate__(self, state):
05. if fullname not in state:
06. first =
07. last =
08. if firstname in state:
09. first = state[firstname]
10. del state[firstname]
11. if lastname in state:
12. last = state[lastname]
13. del state[lastname]
14. self.fullname = .join([first, last]).strip()
15. self.__dict__.(state)
在这个示例,我们添加了一个新的属性 fullname ,并除去了两个现有的属性 firstname 和 lastname 。当对进步前辈步行过 pickle 的实例履行 unpickle 时,其进步前辈步行过 pickle 的状况会作为字典传递给 _setstate_() ,它将包含 firstname 和 lastname 属性的值。接下来,将这两个值组合起来,并将它们分派给新属性 fullname 。在这个过程中,我们删除了状况字典中旧的属性。更新和从头 pickle 进步前辈步行过 pickle 的所有实例之后,如今可以从类定义中除去 _setstate_() 办法。
模块的批改
在概念上,模块的名称或地位的改变类似于类名称的改变,但处理惩罚体式格式却完全不合。那是因为模块的信息存储在 pickle 中,而不是经由过程标准的 pickle 接口就可以批改的属性。事实上,改变模块信息的独一办法是对实际的 pickle 文件本身履行查找和调换操纵。至于如何确切地去做,这取决于具体的操纵体系和可应用的对象。很显然,在这种景象下,您会想备份您的文件,以免产生错误。但这种批改应当很是简单,并且对二进制 pickle 格局进行更改与对文本 pickle 格局进行更改应当一样有效。
所有随风而逝的都属于昨天的,所有历经风雨留下来的才是面向未来的。—— 玛格丽特·米切尔 《飘》
持久性就是指对峙对象,甚至在多次履行同一法度之间也对峙对象。经由过程本文,您会对 Python对象的各类持久性机制(从关系数据库到 Python 的 pickle以及其它机制)有一个总体熟悉。别的,还会让您更深一步地懂得Python 的对象序列化才能。
什么是持久性?
持久性的根蒂根基思惟很简单。假定有一个 Python 法度,它可能是一个经管日常待就事项的法度,您在多次履行这个法度之间可以保存应用法度对象(待就事项)。换句话说,您将对象存储在磁盘上,便于今后检索。这就是持久性。要达到这个目标,有几种办法,每一种办法都有其优毛病。
例如,可以将对象数据存储在某种格局的文本文件中,譬如 CSV 文件。或者可以用关系数据库,譬如 Gadfly、MySQL、PostgreSQL 或者 DB2。这些文件格局和数据库都很是优良,对于所有这些存储机制,Python 都有结实的接口。
这些存储机制都有一个共同点:存储的数据是自力于对这些数据进行操纵的对象和法度。如许做的益处是,数据可以作为共享的资料,供其它应用法度应用。毛病是,用这种体式格式,可以容许其它法度接见对象的数据,这违背了面向对象的封装性原则 — 即对象的数据只能经由过程这个对象自身的公共(public)接口来接见。
别的,对于某些应用法度,关系数据库办法可能不是很幻想。尤其是,关系数据库不睬解对象。相反,关系数据库会强行应用本身的类型体系和关系数据模型(表),每张表包含一组元组(行),每行包含具有固定命目标静态类型字段(列)。若是应用法度的对象模型不克不及够便利地转换到关系模型,那么在将对象映射到元组以及将元组映射回对象方面,会碰着必然难度。这种艰苦常被称为阻碍性不匹配(impedence-mismatch)题目。
对象持久性
若是透明地存储 Python 对象,而不丧失其身份和类型等信息,则须要某种情势的对象序列化:它是一个将随便率性错杂的对象转成对象的文本或二进制默示的过程。同样,必须可以或许将对象经过序列化后的情势恢复到原有的对象。在 Python 中,这种序列化过程称为 pickle,可以将对象 pickle 成字符串、磁盘上的文件或者任何类似于文件的对象,也可以将这些字符串、文件或任何类似于文件的对象 unpickle 成本来的对象。我们将在本文后面具体评论辩论 pickle。
假定您喜好将任何事物都保存成对象,并且避免将对象转换成某种基于非对象存储的开销;那么 pickle 文件可以供给这些益处,但有时可能须要比这种简单的 pickle 文件更结实以及更具有可伸缩性的事物。例如,只用 pickle 不克不及解决定名和查找 pickle 文件如许的题目,别的,它也不克不及支撑并发地接见持久性对象。若是须要这些方面的功能,则要乞助类似于 ZODB(针对 Python 的 Z 对象数据库)这类数据库。ZODB 是一个结实的、多用户的和面向对象的数据库体系,它可以或许存储和经管随便率性错杂的 Python 对象,并支撑事务操纵和并发把握。(请参阅 参考材料,以 ZODB。)令人足够感爱好的是,甚至 ZODB 也依附 Python 的本机序列化才能,并且要有效地应用 ZODB,必须充沛懂得 pickle。
另一种令人感爱好的解决持久性题目的办法是 Prevayler,它最初是用 Java 实现的(有关 Prevaylor 方面的developerWorks 文章,请参阅 参考材料)。比来,一群 Python 法度员将 Prevayler 移植到了 Python 上,另起名为 PyPerSyst,由 SourceForge 托管(有关至 PyPerSyst 项目标链接,请参阅 参考材料)。Prevayler/PyPerSyst 概念也是建树在 Java 和 Python 说话的本机序列化才能之上。PyPerSyst 将全部对象体系保存在内存中,并经由过程不时地将体系快照 pickle 到磁盘以及保护一个号令日记(经由过程此日记可以从头应用新的快照)来供给灾害恢复。所以,尽管应用 PyPerSyst 的应用法度受到可用内存的限制,但益处是本机对象体系可以完全装入到内存中,因而速度极快,并且实现起来要比如 ZODB 如许的数据库简单,ZODB 容许对象的数量比同时在能内存中所对峙的对象要多。
既然我们已经扼要评论辩论了存储持久对象的各类办法,那么如今该具体商量 pickle 过程了。固然我们首要感爱好的是摸索以各类体式格式来保存 Python 对象,而不必将其转换成某种其它格局,但我们仍然还有一些须要存眷的处所,譬如:如何有效地 pickle 和 unpickle 简单对象以及错杂对象,包含定制类的实例;如何保护对象的引用,包含轮回引用和递归引用;以及如何处理惩罚类定义产生的变更,从而应用以前经过 pickle 的实例时不会产生题目。我们将在随后关于 Python 的 pickle 才能商量中涉及所有这些题目。
一些经过 pickle 的 Python
pickle 模块及其同类模块 cPickle 向 Python 供给了 pickle 支撑。后者是用 C 编码的,它具有更好的机能,对于大多半应用法度,推荐应用该模块。我们将持续评论辩论 pickle ,但本文的示例实际是哄骗了 cPickle 。因为此中大多半示例要用 Python shell 来显示,所以先显现一下如何导入 cPickle ,并可以作为 pickle 来引用它:
>>> import cPickle as pickle
如今已经导入了该模块,接下来让我们看一下 pickle 接口。 pickle 模块供给了以下函数对: dumps(object) 返回一个字符串,它包含一个 pickle 格局的对象; loads(string) 返回包含在 pickle 字符串中的对象; dump(object, file) 将对象写到文件,这个文件可所以实际的物理文件,但也可所以任何类似于文件的对象,这个对象具有 write() 办法,可以接管单个的字符串参数; load(file) 返回包含在 pickle 文件中的对象。
缺省景象下, dumps() 和 dump() 应用可打印的 ASCII 默示来创建 pickle。两者都有一个 final 参数(可选),若是为 True ,则该参数指定用更快以及更小的二进制默示来创建 pickle。 loads() 和 load() 函数主动检测 pickle 是二进制格局还是文本格局。
清单 1 显示了一个交互式会话,这里应用了刚才所描述的 dumps() 和 loads() 函数:
清单 1. dumps() 和 loads() 的演示:
01.>>> import cPickle as pickle
02.>>> t1 = (this is a string, 42, [1, 2, 3], None)
03.>>> t1
04.(this is a string, 42, [1, 2, 3], None)
05.>>> p1 = pickle.dumps(t1)
06.>>> p1
07.(Sthis is a string/nI42/n(lp1/nI1/naI2/naI3/naNtp2/n.
08.>>> print p1
09.(Sthis is a string
10.I42
11.(lp1
12.I1
13.aI2
14.aI3
15.aNtp2
16..
17.>>> t2 = pickle.loads(p1)
18.>>> t2
19.(this is a string, 42, [1, 2, 3], None)
20.>>> p2 = pickle.dumps(t1, True)
21.>>> p2
22.(U/x10this is a stringK]q/x01(K/x01K/x02K/x03eNtq/x02.
23.>>> t3 = pickle.loads(p2)
24.>>> t3
25.(this is a string, 42, [1, 2, 3], None)
注:该文本 pickle 格局很简单,这里就不说了然。事实上,在 pickle 模块中记录了所有应用的商定。我们还应当指出,在我们的示例中应用的都是简单对象,是以应用二进制 pickle 格局不会在节俭空间上显示出太大的效力。然而,在实际应用错杂对象的体系中,您会看到,应用二进制格局可以在大小和速度方面带来明显的改进。
接下来,我们看一些示例,这些示例用到了 dump() 和 load() ,它们应用文件和类似文件的对象。这些函数的操纵很是类似于我们刚才所看到的 dumps() 和 loads() ,差别在于它们还有另一种才能 — dump() 函数能一个接着一个地将几个对象转储到同一个文件。随后调用 load() 来以同样的次序检索这些对象。清单 2 显示了这种才能的实际应用:
清单 2. dump() 和 load() 示例:
01.>>> a1 = apple
02.>>> b1 = {1: One, 2: Two, 3: Three}
03.>>> c1 = [fee, fie, foe, fum]
04.>>> f1 = file(temp.pkl, wb)
05.>>> pickle.dump(a1, f1, True)
06.>>> pickle.dump(b1, f1, True)
07.>>> pickle.dump(c1, f1, True)
08.>>> f1.close()
09.>>> f2 = file(temp.pkl, rb)
10.>>> a2 = pickle.load(f2)
11.>>> a2
12.apple
13.>>> b2 = pickle.load(f2)
14.>>> b2
15.{1: One, 2: Two, 3: Three}
16.>>> c2 = pickle.load(f2)
17.>>> c2
18.[fee, fie, foe, fum]
19.>>> f2.close()
Pickle 的威力
到今朝为止,我们讲述了关于 pickle 方面的根蒂根基常识。在这一节,将评论辩论一些高等题目,当您开端 pickle 错杂对象时,会碰到这些题目,此中包含定制类的实例。荣幸的是,Python 可以很轻易地处理惩罚这种景象。
可移植性
从空间和时候上说,Pickle 是可移植的。换句话说,pickle 文件格局自力于机械的体系布局,这意味着,例如,可以在 Linux 下创建一个 pickle,然后将它发送到在 Windows 或 Mac OS 下运行的 Python 法度。并且,当进级到更新版本的 Python 时,不必愁闷可能要放弃已有的 pickle。Python 开辟人员已经包管 pickle 格局将可以向后兼容 Python 各个版本。事实上,在 pickle 模块中供给了有关今朝以及所支撑的格局方面的具体信息.
清单 3. 检索所支撑的格局:
01.>>> pickle.format_version
02.1.3
03.>>> pickle.compatible_formats
04.[1.0, 1.1, 1.2]
多个引用,同一对象
在 Python 中,变量是对象的引用。同时,也可以用多个变量引用同一个对象。经证实,Python 在用经过 pickle 的对象保护这种行动方面涓滴没有艰苦,如清单 4 所示:
清单 4. 对象引用的保护:
01.>>> a = [1, 2, 3]
02.>>> b = a
03.>>> a
04.[1, 2, 3]
05.>>> b
06.[1, 2, 3]
07.>>> a.append(4)
08.>>> a
09.[1, 2, 3, 4]
10.>>> b
11.[1, 2, 3, 4]
12.>>> c = pickle.dumps((a, b))
13.>>> d, e = pickle.loads(c)
14.>>> d
15.[1, 2, 3, 4]
16.>>> e
17.[1, 2, 3, 4]
18.>>> d.append(5)
19.>>> d
20.[1, 2, 3, 4, 5]
21.>>> e
22.[1, 2, 3, 4, 5]
轮回引用和递归引用
可以将刚才演示过的对象引用支撑扩大到 轮回引用(两个对象各自包含对对方的引用)和 递归引用(一个对象包含对其自身的引用)。下面两个清单侧重显示这种才能。我们先看一下递归引用:
清单 5. 递归引用:
01.>>> l = [1, 2, 3]
02.>>> l.append(l)
03.>>> l
04.[1, 2, 3, [...]]
05.>>> l[3]
06.[1, 2, 3, [...]]
07.>>> l[3][3]
08.[1, 2, 3, [...]]
09.>>> p = pickle.dumps(l)
10.>>> l2 = pickle.loads(p)
11.>>> l2
12.[1, 2, 3, [...]]
13.>>> l2[3]
14.[1, 2, 3, [...]]
15.>>> l2[3][3]
16.[1, 2, 3, [...]]
如今,看一个轮回引用的示例:
清单 6. 轮回引用:
01.>>> a = [1, 2]
02.>>> b = [3, 4]
03.>>> a.append(b)
04.>>> a
05.[1, 2, [3, 4]]
06.>>> b.append(a)
07.>>> a
08.[1, 2, [3, 4, [...]]]
09.>>> b
10.[3, 4, [1, 2, [...]]]
11.>>> a[2]
12.[3, 4, [1, 2, [...]]]
13.>>> b[2]
14.[1, 2, [3, 4, [...]]]
15.>>> a[2] is b
16.1
17.>>> b[2] is a
18.1
19.>>> f = file(temp.pkl, w)
20.>>> pickle.dump((a, b), f)
21.>>> f.close()
22.>>> f = file(temp.pkl, r)
23.>>> c, d = pickle.load(f)
24.>>> f.close()
25.>>> c
26.[1, 2, [3, 4, [...]]]
27.>>> d
28.[3, 4, [1, 2, [...]]]
29.>>> c[2]
30.[3, 4, [1, 2, [...]]]
31.>>> d[2]
32.[1, 2, [3, 4, [...]]]
33.>>> c[2] is d
34.1
35.>>> d[2] is c
36.1
重视,若是分别 pickle 每个对象,而不是在一个元组中一路 pickle 所有对象,会获得略微不合(但很首要)的成果,如清单 7 所示:
清单 7. 分别 pickle vs. 在一个元组中一路 pickle:
01.>>> f = file(temp.pkl, w)
02.>>> pickle.dump(a, f)
03.>>> pickle.dump(b, f)
04.>>> f.close()
05.>>> f = file(temp.pkl, r)
06.>>> c = pickle.load(f)
07.>>> d = pickle.load(f)
08.>>> f.close()
09.>>> c
10.[1, 2, [3, 4, [...]]]
11.>>> d
12.[3, 4, [1, 2, [...]]]
13.>>> c[2]
14.[3, 4, [1, 2, [...]]]
15.>>> d[2]
16.[1, 2, [3, 4, [...]]]
17.>>> c[2] is d
18.0
19.>>> d[2] is c
20.0
相等,但并不老是雷同
正如在上一个示例所暗示的,只有在这些对象引用内存中同一个对象时,它们才是雷同的。在 pickle 景象中,每个对象被恢复到一个与本来对象相等的对象,但不是同一个对象。换句话说,每个 pickle 都是本来对象的一个副本:
清单 8. 作为本来对象副本的被恢复的对象:
01.>>> j = [1, 2, 3]
02.>>> k = j
03.>>> k is j
04.1
05.>>> x = pickle.dumps(k)
06.>>> y = pickle.loads(x)
07.>>> y
08.[1, 2, 3]
09.>>> y == k
10.1
11.>>> y is k
12.0
13.>>> y is j
14.0
15.>>> k is j
16.1
同时,我们看到 Python 可以或许保护对象之间的引用,这些对象是作为一个单位进行 pickle 的。然而,我们还看到分别调用 dump() 会使 Python 无法保护对在该单位外部进行 pickle 的对象的引用。相反,Python 复制了被引用对象,并将副本和被 pickle 的对象存储在一路。对于 pickle 和恢复单个对象层次布局的应用法度,这是没有题目的。但要意识到还有其它景象。
值得指出的是,有一个选项确切容许分别 pickle 对象,并保护彼此之间的引用,只要这些对象都是 pickle 到同一文件即可。 pickle 和 cPickle 模块供给了一个 Pickler (与此相对应是 Unpickler ),它可以或许跟踪已经被 pickle 的对象。经由过程应用这个 Pickler ,将会经由过程引用而不是经由过程值来 pickle 共享和轮回引用:
清单 9. 保护分别 pickle 的对象间的引用:
01.>>> f = file(temp.pkl, w)
02.>>> pickler = pickle.Pickler(f)
03.>>> pickler.dump(a)
04.<cPickle.Pickler object at 0 x89b0bb8>
05.>>> pickler.dump(b)
06.<cPickle.Pickler object at 0 x89b0bb8>
07.>>> f.close()
08.>>> f = file(temp.pkl, r)
09.>>> unpickler = pickle.Unpickler(f)
10.>>> c = unpickler.load()
11.>>> d = unpickler.load()
12.>>> c[2]
13.[3, 4, [1, 2, [...]]]
14.>>> d[2]
15.[1, 2, [3, 4, [...]]]
16.>>> c[2] is d
17.1
18.>>> d[2] is c
19.1
不成 pickle 的对象
一些对象类型是不成 pickle 的。例如,Python 不克不及 pickle 文件对象(或者任何带有对文件对象引用的对象),因为 Python 在 unpickle 时不克不及包管它可以重建该文件的状况(另一个示例斗劲难懂,在这类文章中不值得提出来)。试图 pickle 文件对象会导致以下错误:
清单 10. 试图 pickle 文件对象的成果:
01.>>> f = file(temp.pkl, w)
02.>>> p = pickle.dumps(f)
03.Traceback (most recent call last):
04. File <input>, line 1, in ?
05. File /usr/lib/python2.2/copy_reg.py, line 57, in _reduce
06. raise TypeError, cant pickle %s objects % base.__name__
07.TypeError: cant pickle file objects
类实例
与 pickle 简单对象类型比拟,pickle 类实例要多加留心。这首要因为 Python 会 pickle 实例数据(凡是是 _dict_ 属性)和类的名称,而不会 pickle 类的代码。当 Python unpickle 类的实例时,它会试图应用在 pickle 该实例时的确切的类名称和模块名称(包含任何包的路径前缀)导入包含该类定义的模块。别的要重视,类定义必须呈如今模块的最顶层,这意味着它们不克不及是嵌套的类(在其它类或函数中定义的类)。
当 unpickle 类的实例时,凡是不会再调用它们的 _init_() 办法。相反,Python 创建一个通用类实例,并应用已进行过 pickle 的实例属性,同时设置该实例的 _class_ 属性,使其指向本来的类。
对 Python 2.2 中引入的新型类进行 unpickle 的机制与本来的略有不合。固然处理惩罚的成果实际上与对旧型类处理惩罚的成果雷同,但 Python 应用 copy_reg 模块的 _reconstructor() 函数来恢复新型类的实例。
若是对新型或旧型类的实例批改缺省的 pickle 行动,则可以定义特别的类的办法 _getstate_() 和 _setstate_() ,在保存和恢复类实例的状况信息时代,Python 会调用这些办法。在以下几节中,我们会看到一些示例哄骗了这些特别的办法。
如今,我们看一个简单的类实例。起首,创建一个 persist.py 的 Python 模块,它包含以下新型类的定义:
清单 11. 新型类的定义:
01.class Foo(object):
02. def __init__(self, value):
03. self.value = value
如今可以 pickle Foo 实例,并看一下它的默示:
清单 12. pickle Foo 实例:
01.>>> import cPickle as pickle
02.>>> Orbtech.examples.persist import Foo
03.>>> foo = Foo(What is a Foo?)
04.>>> p = pickle.dumps(foo)
05.>>> print p
06.ccopy_reg
07._reconstructor
08.p1
09.(cOrbtech.examples.persist
10.Foo
11.p2
12.c__builtin__
13.object
14.p3
15.NtRp4
16.(dp5
17.Svalue
18.p6
19.SWhat is a Foo?
20.sb.
可以看到这个类的名称 Foo 和全限制的模块名称 Orbtech.examples.persist 都存储在 pickle 中。若是将这个实例 pickle 成一个文件,稍后再 unpickle 它或在另一台机械上 unpickle,则 Python 会试图导入 Orbtech.examples.persist 模块,若是不克不及导入,则会抛出异常。若是重定名该类和该模块或者将该模块移到另一个目次,则也会产生类似的错误。
这里有一个 Python 发失足误消息的示例,重定名 Foo 类,然后试图装入进步前辈步行过 pickle 的 Foo 实例时会产生该错误:
清单 13. 试图装入一个被重定名的 Foo 类的经过 pickle 的实例:
01.>>> import cPickle as pickle
02.>>> f = file(temp.pkl, r)
03.>>> foo = pickle.load(f)
04.Traceback (most recent call last):
05. File <input>, line 1, in ?
06.AttributeError: module object has no attribute Foo
在重定名 persist.py 模块之后,也会产生类似的错误:
清单 14. 试图装入一个被重定名的 persist.py 模块的经过 pickle 的实例:
01.>>> import cPickle as pickle
02.>>> f = file(temp.pkl, r)
03.>>> foo = pickle.load(f)
04.Traceback (most recent call last):
05. File <input>, line 1, in ?
06.ImportError: No module named persist
我们会鄙人面 模式改进这一节供给一些技巧来经管这类更改,而不会破损现有的 pickle。
特别的状况办法
前面提到对一些对象类型(譬如,文件对象)不克不及进行 pickle。处理惩罚这种不克不及 pickle 的对象的实例属性时可以应用特别的办法( _getstate_() 和 _setstate_() )来批改类实例的状况。这里有一个 Foo 类的示例,我们已经对它进行了批改以处理惩罚文件对象属性:
清单 15. 处理惩罚不克不及 pickle 的实例属性:
01.class Foo(object):
02. def __init__(self, value, filename):
03. self.value = value
04. self.logfile = file(filename, w)
05. def __getstate__(self):
06. Return state values to be pickled.
07. f = self.logfile
08. return (self.value, f.name, f.tell())
09. def __setstate__(self, state):
10. Restore state the unpickled state values.
11. self.value, name, position = state
12. f = file(name, w)
13. f.seek(position)
14. self.logfile = f
pickle Foo 的实例时,Python 将只 pickle 当它调用该实例的 _getstate_() 办法时返回给它的值。类似的,在 unpickle 时,Python 将供给经过 unpickle 的值作为参数传递给实例的 _setstate_() 办法。在 _setstate_() 办法内,可以按照经过 pickle 的名称和地位信息来重建文件对象,并将该文件对象分派给这个实例的 logfile 属性。
模式改进
跟着时候的推移,您会发明本身必必要更改类的定义。若是已经对某个类实例进行了 pickle,而如今又须要更改这个类,则您可能要检索和更新那些实例,以便它们能在新的类定义下持续正常工作。而我们已经看到在对类或模块进行某些更改时,会呈现一些错误。荣幸的是,pickle 和 unpickle 过程供给了一些 hook,我们可以用它们来支撑这种模式改进的须要。
在这一节,我们将商量一些办法来猜测常见题目以及如何解决这些题目。因为不克不及 pickle 类实例代码,是以可以添加、更改和除去办法,而不会影响现有的经过 pickle 的实例。出于同样的原因,可以不必愁闷类的属性。您必须确保包含类定义的代码模块在 unpickle 景象中可用。同时还必须为这些可能导致 unpickle 题目的更改做好规划,这些更改包含:更改类名、添加或除去实例的属性以及改变类定义模块的名称或地位。
类名的更改
要更改类名,而不破损先前经过 pickle 的实例,请遵守以下步调。起首,确保本来的类的定义没有被更改,以便在 unpickle 现有实例时可以找到它。不要更改本来的名称,而是在与本来类定义地点的同一个模块中,创建该类定义的一个副本,同时给它一个新的类名。然后应用实际的新类名来调换 NewClassName ,将以下办法添加到本来类的定义中:
清单 16. 更改类名:添加到本来类定义的办法:
01.def __setstate__(self, state):
02. self.__dict__.(state)
03. self.__class__ = NewClassName
当 unpickle 现有实例时,Python 将查找本来类的定义,并调用实例的 _setstate_() 办法,同时将给新的类定义从头分派该实例的 _class_ 属性。一旦断定所有现有的实例都已经 unpickle、更新和从头 pickle 后,可以从源代码模块中除去旧的类定义。
属性的添加和删除
这些特别的状况办法 _getstate_() 和 _setstate_() 再一次使我们能把握每个实例的状况,并使我们有机会处理惩罚实例属性中的更改。让我们看一个简单的类的定义,我们将向其添加和除去一些属性。这是是最初的定义:
清单 17. 最初的类定义:
01.class Person(object):
02. def __init__(self, firstname, lastname):
03. self.firstname = firstname
04. self.lastname = lastname
假定已经创建并 pickle 了 Person 的实例,如今我们决意真的只想存储一个名称属性,而不是分别存储姓和名。这里有一种体式格式可以更改类的定义,它将先前经过 pickle 的实例迁徙到新的定义:
清单 18. 新的类定义:
01.class Person(object):
02. def __init__(self, fullname):
03. self.fullname = fullname
04. def __setstate__(self, state):
05. if fullname not in state:
06. first =
07. last =
08. if firstname in state:
09. first = state[firstname]
10. del state[firstname]
11. if lastname in state:
12. last = state[lastname]
13. del state[lastname]
14. self.fullname = .join([first, last]).strip()
15. self.__dict__.(state)
在这个示例,我们添加了一个新的属性 fullname ,并除去了两个现有的属性 firstname 和 lastname 。当对进步前辈步行过 pickle 的实例履行 unpickle 时,其进步前辈步行过 pickle 的状况会作为字典传递给 _setstate_() ,它将包含 firstname 和 lastname 属性的值。接下来,将这两个值组合起来,并将它们分派给新属性 fullname 。在这个过程中,我们删除了状况字典中旧的属性。更新和从头 pickle 进步前辈步行过 pickle 的所有实例之后,如今可以从类定义中除去 _setstate_() 办法。
模块的批改
在概念上,模块的名称或地位的改变类似于类名称的改变,但处理惩罚体式格式却完全不合。那是因为模块的信息存储在 pickle 中,而不是经由过程标准的 pickle 接口就可以批改的属性。事实上,改变模块信息的独一办法是对实际的 pickle 文件本身履行查找和调换操纵。至于如何确切地去做,这取决于具体的操纵体系和可应用的对象。很显然,在这种景象下,您会想备份您的文件,以免产生错误。但这种批改应当很是简单,并且对二进制 pickle 格局进行更改与对文本 pickle 格局进行更改应当一样有效。
所有随风而逝的都属于昨天的,所有历经风雨留下来的才是面向未来的。—— 玛格丽特·米切尔 《飘》