python对象复制

来源:互联网 时间:1970-01-01

需求:

你想复制一个对象.因为在Python中,无论你把对象做为参数传递,做为函数返回值,都是引用传递的.

讨论:

标准库中的copy模块提供了两个方法来实现拷贝.一个方法是copy,它返回和参数包含内容一样的对象.


import copy
new_list = copy.copy(existing_list)

有些时候,你希望对象中的属性也被复制,可以使用deepcopy方法:

import copy
new_list_of_dicts = copy.deepcopy(existing_list_of_dicts)

当你对一个对象赋值的时候(做为参数传递,或者做为返回值),Python和Java一样,总是传递原始对象的引用,而不是一个副本.其它一些语言当赋值的时候总是传递副本.Python从不猜测用户的需求 ,如果你想要一个副本,你必须显式的要求.
Python的行为很简单,迅速,而且一致.然而,如果你需要一个对象拷贝而并没有显式的写出来,会出现问题的,比如:
>>> a = [1, 2, 3]
>>> b = a
>>> b.append(5)
>>> print a, b
[1, 2, 3, 5] [1, 2, 3, 5]
在这里,变量a和b都指向同一个对象(一个列表),所以,一旦你修改了二者之一,另外一个也会受到影响.无论怎样,都会修改原来的对象.
注意:
要想成为一个Python高手,首先要注意的问题就是对象的变更操作和赋值,它们都是针对对象的引用操作的.一个语句比如a = []将a重新绑定给一个新对象,但不会影响以前的对象.然而,对象复制却不同,当对象复制后,对象变更操作就有了区别. 
如 果你想修改一个对象,而且想让原始的对象不受影响,那你就需要对象复制.正如本节说的一样,你可以使用copy模块中的两个方法来实现需求.一般的,可以 使用copy.copy,它可以进行对象的浅复制(shallow copy),它复制了对象,但对于对象中的元素,依然使用引用.
浅复制,有时无法获得一个和原来对象完全一致的副本,如果你想修改对象中的元素,不仅仅是对象本身的话:

>>> list_of_lists = [ ['a'], [1, 2], ['z', 23] ]
>>> copy_lol = copy.copy(lists_of_lists) 
>>> copy_lol[1].append('boo') 
>>> print list_of_lists, copy_lol 
[['a'], [1, 2, 'boo'], ['z', 23]] [['a'], [1, 2, 'boo'], ['z', 23]] 

在这里,变量list_of_lists,copy_lol指向了两个不同的对象,所以我们可以修改它们任何一个, 而不影响另外一个.然而,如果我们修改了一个对象中的元素,那么另一个也会受影响,因为它们中的元素还是共享引用.
如果你希望复制一个容器对象,以及它里面的所有元素(包含元素的子元素),使用copy.deepcopy,这个方法会消耗一些时间和空间,不过,如果你需要完全复制,这是唯一的方法.
对于一般的浅拷贝,使用copy.copy就可以了,当然,你需要了解你要拷贝的对象.要复制列表L,使用list(L),要复制一个字典d,使用dict(d),要复制一个集合s,使用set(s),这样,我们总结出一个规律,如果你要复制一个对象o,它属于内建的类型t,那么你可以使用t(o)来 获得一个拷贝.dict也提供了一个复制版本,dict.copy,这个和dict(d)是一样,我推荐你使用后者,这个使得代码更一致,而且还少几个字符.
要复制一个别的类型,无论是你自己写的还是使用库中的,使用copy.copy,如果你自己写一个类,没有必要费神去写clone和copy函数,如果你想定义自己的类复制的方式,实现一个__copy__,或者__getstate__和__setstate__.如果你想定义自己类型的deepcopy,实现方法__deepcopy__. 
注意你不用复制不可修改对象(string,数字,元组),因为你不用担心修改它们.如果你想尝试一下复制,依然会得到原来的.虽然无伤大雅,不过真的浪费尽力:

>>> s = 'cat' 
>>> t = copy.copy(s) 
>>> s is t 
True

is操作符用于不仅判断两个对象是否完全一致,而且是同一个对象(is判断标识符,要比较内容,使用==),判断标识符是否相等对于不可修改对象没有什么意义.然而 ,判断标识符对于可修改对象有时候是很重要的,比如,你不确定a和b是否指向同一个对象,使用a is b会立刻得到结果.这样你可以自己判断是否需要使用对象拷贝.
注意:
你可以使用另一种拷贝方式,给定一个列表L,无论是完整切片L[:]或者列表解析[x for x in L],都会获得L的浅拷贝,试试L+[],L*1...但是上面两种方法都会使人迷惑,使用list(L)最清晰和快速,当然,由于历史原因,你可能会经常看到L[:]的写法. 
对于dict,你可能见过下面的复制方法:
>>> for somekey in d: 
... d1[somekey] = d[somekey]

或者更简单一些的方法,d1={},d1.update(d),无论怎样,这些代码都是缺乏效率的,使用d1=dict(d)吧.

相关说明:

copy(x)
    Shallow copy operation on arbitrary Python objects.

    See the module's __doc__ string for more info. 

deepcopy(x, memo=None, _nil=[])
    Deep copy operation on arbitrary Python objects.

    See the module's __doc__ string for more info.

相关阅读:
Top