有些时候,特别是处理可变对象时,你可能想拷贝一个对象,改变这个对象而不影响原有的对象。这时就需要用到Python的 copy 模块了。然而(幸运的是),Python模块并不具有感知能力, 因此我们不用担心某天基于Linux的机器人崛起。但是我们的确需要告诉Python如何有效率地拷贝对象。
__copy__(self)
定义对类的实例使用 copy.copy() 时的行为。 copy.copy() 返回一个对象的浅拷贝,这意味着拷贝出的实例是全新的,然而里面的数据全都是引用的。也就是说,对象本身是拷贝的,但是它的数据还是引用的(所以浅拷贝中的数据更改会影响原对象)。
__deepcopy__(self, memodict=)
定义对类的实例使用 copy.deepcopy() 时的行为。 copy.deepcopy() 返回一个对象的深拷贝,这个对象和它的数据全都被拷贝了一份。 memodict 是一个先前拷贝对象的缓存,它优化了拷贝过程,而且可以防止拷贝递归数据结构时产生无限递归。当你想深拷贝一个单独的属性时,在那个属性上调用 copy.deepcopy() ,使用 memodict 作为第一个参数。
这些魔法方法有什么用武之地呢?像往常一样,当你需要比默认行为更加精确的控制时。例如,如果你想拷贝一个对象,其中存储了一个字典作为缓存(可能会很大),拷贝缓存可能是没有意义的。如果这个缓存可以在内存中被不同实例共享,那么它就应该被共享。
13. Pickling
如果你和其他的Python爱好者共事过,很可能你已经听说过Pickling了。Pickling了。Pickling是Python数据结构的序列化过程,当你想存储一个对象稍后再取出读取时,Pickling会显得十分有用。然而它同样也是担忧和混淆的主要来源。
Pickling是如此的重要,以至于它不仅仅有自己的模块( pickle ),还有自己的协议和魔法方法。首先,我们先来简要的介绍一下如何pickle已存在的对象类型(如果你已经知道了,大可跳过这部分内容)。
13.1. Pickling : 小试牛刀
我们一起来pickle吧。假设你有一个字典,你想存储它,稍后再取出来。你可以把它的内容写入一个文件,小心翼翼地确保使用了正确地格式,要把它读取出来,你可以使用 exec() 或处理文件输入。但是这种方法并不可靠:如果你使用纯文本来存储重要数据,数据很容易以多种方式被破坏或者修改,导致你的程序崩溃,更糟糕的情况下,还可能在你的计算机上运行恶意代码。因此,我们要pickle它:
import pickle data = {'foo': [1,2,3], 'bar': ('Hello', 'world!'), 'baz': True} jar = open('data.pkl', 'wb') pickle.dump(data, jar) # 将pickle后的数据写入jar文件 jar.close()过了几个小时,我们想把它取出来,我们只需要反pickle它:
import pickle pkl_file = open('data.pkl', 'rb') # 与pickle后的数据连接 data = pickle.load(pkl_file) # 把它加载进一个变量 print data pkl_file.close()将会发生什么?正如你期待的,它就是我们之前的 data 。
现在,还需要谨慎地说一句: pickle并不完美。Pickle文件很容易因为事故或被故意的破坏掉。Pickling或许比纯文本文件安全一些,但是依然有可能被用来运行恶意代码。而且它还不支持跨Python版本,所以不要指望分发pickle对象之后所有人都能正确地读取。然而不管怎么样,它依然是一个强有力的工具,可以用于缓存和其他类型的持久化工作。
13.2. Pickle你的对象
Pickle不仅仅可以用于内建类型,任何遵守pickle协议的类都可以被pickle。Pickle协议有四个可选方法,可以让类自定义它们的行为(这和C语言扩展略有不同,那不在我们的讨论范围之内)。
__getinitargs__(self)