在python中,当你对一个对象赋值,将其作为参数传递,或者作为结果返回时,python通常会使用指向原对象的引用(即把新对象的内存地址指向原对象),而不是真正的拷贝。其它的一些语言则在每次赋值时都都进行拷贝操作。在python中不为赋值操作进行“隐式”的拷贝,要得到一个拷贝,必须明确的要求进行拷贝。

      所以,在python中,默认情况下对象赋值为浅拷贝即对象引用,而深拷贝则为真正的对象拷贝。



一,我们先来了解一下id函数,官方解释如下:

id(object)
Return the “identity” of an object. This is an integer (or long integer) which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.
CPython implementation detail: This is the address of the object in memory.

简而言之,id函数就是返回对象在自己生命周期中的内存地址。下面看两个例子:

In [48]: print id('abc')
3074738896
In [49]: cmd='abc'
In [50]: print id(cmd)
3074738896

In [51]: x='bbc'
In [52]: y='bbc'
In [54]: print id(x),id(y),id('bbc')
3052874184 3052874184 3052874184

在Python中一切皆对象,像字符串abc,数字这样的值都是对象,只不过数字是一个整型对象,而'abc'是一个字符串对象。上面的赋值操作cmd='abc',在Python中实际的处理过程是这样的:

先在内存中搜索是否存在字符串abc,如果字符串abc已经在内存中存在了,那就直接返回已存在的字符串abc的内存地址,让变量cmd指向这个已存在字符串abc的内存地址。如果内存中不存在字符串abc,就先申请一段内存分配给一个字符串对象来存储字符串abc,然后让变量cmd去指向这个对象,实际上就是指向这段内存(这里有点和C语言中的指针类似)。这样做的好处,就是提升了速度。

而id(‘abc’)和id(cmd)的结果一样,说明id函数在作用于任何对象(字符串对象或是变量对象)时,其返回的都是对象指向的内存地址。



二,浅拷贝案例:

默认情况下,我们向新对象中插入数据,则老对象也会更新(当然向原对象插入数据,新对象也会更新):

In [37]: a=[1,2,3]
In [38]: b=a
In [39]: b.append(333)
In [40]: print a
[1, 2, 3, 333]
In [41]: print b
[1, 2, 3, 333]

In [42]: print id(a),id(b)
3054610764 3054610764

结论:

我们通过id函数看到,两个对象a,b指向同一片内存地址空间。即他们a和b对象之间的赋值操作属于引用,不是拷贝。故修改任意一个对象,另一个对象也会随之改变。



三,深拷贝案例

我们可以通过如下方法来解决对象引用的问题:

In [62]: import copy

In [63]: a=[1,2,3]
In [64]: b=copy.deepcopy(a)
In [65]: b.append(333)
In [66]: print a
[1, 2, 3]
In [67]: print b
[1, 2, 3, 333]
In [68]: print id(a),id(b)
3064057292 3064057644

我们需要使用copy.deepcopy方法来显示的进行对象拷贝,这样就不会出现前拷贝的问题。



四,判断两个对象是否是同一个

1,可以通过id函数来判断

2,使用如下方式

In [72]: a=[1,2,3]
In [73]: b=a
In [74]: a is b
Out[74]: True


In [75]: a=[1,2,3]
In [76]: b=copy.deepcopy(a)
In [77]: a is b
Out[77]: False