Python拷贝对象(浅拷贝copy与深拷贝deepcopy)

时间:2023-02-15 19:51:40

先说一段废话。Python中的参数传递都是对象引用传递,这种方式相当于传值和传引用的一种综合。如果函数收到的是一个可变对象(比如字典或者列表)的引用,就能修改对象的原始值——相当于通过“传引用”来传递对象。如果函数收到的是一个不可变对象(比如数字、字符或者元组)的引用,就不能直接修改原始对象——相当于通过“传值”来传递对象。

Python通过引用计数机制实现自动垃圾回收功能,Python中的每个对象都有一个引用计数,用来计数该对象在不同场所分别被引用了多少次。每当引用一次Python对象,相应的引用计数就增1,每当消毁一次Python对象,则相应的引用就减1,只有当引用计数为零时,才真正从内存中删除Python对象。

Python提供了3种复制方法,最常见的=、copy.copy()、copy.deepcopy()。下面通过一段代码来看这三种复制方法的异同。

#!/usr/bin/python
import copy

def fun(list):
    print(id(list))
    for li in list:
        print id(li),

if __name__ == '__main__':
    a = [x for x in range(5)]
    a.append({'name':'zhangsan', 'age':20})
    print id(a)
    for x in a:
        print id(x),
    print ''
    print '----------'
    fun(a)
    print ''
    print '----------'
    fun(copy.copy(a))
    print ''
    print '----------'
    fun(copy.deepcopy(a))
    print ''
这里定义了一个列表a,前面的元素的不可变类型的int,在最后append了一个可变类型dict(),然后打印出各个地方列表a和元素的id值。输出结果如下:

Python拷贝对象(浅拷贝copy与深拷贝deepcopy)

Python拷贝对象(浅拷贝copy与深拷贝deepcopy)

打印出了各个对象的ID值,如果id一样,毫无疑问就是同一个对象。当调用默认复制函数时,形参的id和实参的id是相同的(红色框),也就是说的同一个对象,而调用copy模块拷贝时形参id和实参id不同,也就是说是不同的对象。注意观察列表中的最后一个dict元素。默认的=复制和copy复制得到的id是相同的,也就是说他们是同一个对象,而deepcopy的id和其他都不一样,也就是说是新对象。

再看一个代码:

import copy
#!/usr/bin/python
#encoding=utf-8
a = [1, 2, 3, 4, ['a', 'b']] 

b = a 
c = copy.copy(a) 
d = copy.deepcopy(a) 

a.append(5) 
a[4].append('c') 

print 'a = ', a
print 'b = ', b
print 'c = ', c
print 'd = ', d
最后输出

a =  [1, 2, 3, 4, ['a', 'b', 'c'], 5]
b =  [1, 2, 3, 4, ['a', 'b', 'c'], 5]
c =  [1, 2, 3, 4, ['a', 'b', 'c']]
d =  [1, 2, 3, 4, ['a', 'b']]
所以说copy会对原对象拷贝,但不会递归深拷贝,而deepcopy是递归深拷贝的,这么一来copy是介于=和deepcopy的,用的肯定不多。

1. copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象。 
2. copy.deepcopy 深拷贝 拷贝对象及其子对象