先说一段废话。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值。输出结果如下:
打印出了各个对象的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 深拷贝 拷贝对象及其子对象