Python中浅拷贝,深拷贝的区别
以列表为例, 三者的区别主要在新列表中元素跟原来列表中元素是否仍有联系
1, 对元组类型来说,使用 tuple() 或者切片操作符':',它会返回一个指向相同元组的引用, 内存地址的引用计数会加1,
但copy前后的对象指向的是同一块内存地址, 也就不会有区别了.
2. 浅拷贝, 原列表中可变元素的操作会影响新列表中元素
以列表为例, 首先会初始化一块新的内存地址, 这里存的是原列表的地址,
新的列表指向的是新内存地址, 这跟原列表的地址是不一样的
副作用, copy前的对象影响新的对象
l1 = [[1, 2], (30, 40)]
l2 = list(l1)
l1.append(100)
l1[0].append(3)
l1 #[[1, 2, 3], (30, 40), 100]
l2 #[[1, 2, 3], (30, 40)]
3. 深拷贝, 原列表元素完全不会影响新列表中元素
初始化一块新的内存地址, 新的列表指向这一块新的内存地址,
然后会将原列表里元素的值拷贝到新的内存中, 完全不会受到原列表的影响
浅拷贝
- 下面四种形式是等价的
new_list = old_list.copy()
new_list = old_list[:]
new_list = list(old_list)
new_list = copy.copy(old_list) - 上面四种形式得到的结果是一致的,对简单类型元素按值赋值,符合类型元素复制其地址
import copy \n new_list = copy.copy(old_list)比list.copy()慢一点,
区别在于copy模块需要先确定old_list是列表类型 - 因为在复制完成后修改a中第一个元素的值, b中元素值没有改变
所以b有自己的内存,简单类型的元素’foo’是在b的新分配的内存区域中, 不同于a中’foo’的内存地址
因为在复制完成后修改a中类实例的属性, b中类属性也改变了
所以我们得到: b的复合类型对象Foo类实例foo指向a中foo实例的地址
深拷贝
- 形式 new_list = copy.deepcopy(old_list)
- 对简单类型元素按值赋值,符合类型元素递归的复制其值到新列表中
- 因为在复制完成后修改a中类实例的属性, b中类实例的属性没有改变
所以我们得到: b的复合类型对象Foo类实例不同于a中foo实例的地址
参考
Code
import copy
class Foo(object):
def __init__(self, val):
self.val = val
def __repr__(self):
return str(self.val)
foo = Foo(1)
a = ['foo', foo]
# ae只是a的别名, ae自己没有独立的内存,仍然使用a的内存, 二者的内存地址是一样的
ae = a
# 因为在复制完成后修改a中第一个元素的值, b中元素值没有改变
# 所以b有自己的内存,简单类型的元素'foo'是在b的新分配的内存区域中, 不同于a中'foo'的内存地址
# 因为在复制完成后修改a中类实例的属性, b中类属性也改变了
# 所以我们得到: b的复合类型对象Foo类实例foo指向a中foo实例的地址
b = a.copy()
c = a[:]
d = list(a)
e = copy.copy(a)
# 因为在复制完成后修改a中类实例的属性, b中类实例的属性没有改变
# 所以我们得到: b的复合类型对象Foo类实例不同于a中foo实例的地址
f = copy.deepcopy(a)
# edit orignal list and instance
a.append('baz')
a[0] = 'foo modified'
foo.val = 5
print(' original: %r\n equal: %r\n list.copy(): %r\n slice: %r\n list(): %r\n copy: %r\n deepcopy: %r'
% (a, ae, b, c, d, e, f))
'''
original: ['foo modified', 5, 'baz']
equal: ['foo modified', 5, 'baz']
list.copy(): ['foo', 5]
slice: ['foo', 5]
list(): ['foo', 5]
copy: ['foo', 5]
deepcopy: ['foo', 1]
'''