Python Cookbook 4.1 复制(拷贝)对象(浅复制和深复制)

需求:

你想复制一个对象.由于在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.
相关文章
相关标签/搜索