赞
踩
有时在进行 Python 编程时,会不会觉得程序的逻辑没有任何问题,但结果却总不是预期想要的?原因很可能就出现在赋值语句中!
首先从概念上直观来感受一下直接赋值、浅拷贝与深拷贝的区别:
稍显抽象,但通过几个具体例子就可以理解的非常清楚。
a = {'Roma': ['I', 'V']}
b = a
id(a), id(b)
"""
>> (3121395107904, 3121395107904)
"""
b['Roma'].append('X')
print(a)
print(b)
"""
>> {'Roma': ['I', 'V', 'X']}
>> {'Roma': ['I', 'V', 'X']}
"""
直接赋值是对原对象(这里的 a)的引用,即相当于 b 是 a 的别名,除了名字以外并无任何区别,它们都指向同一块内存地址,可以通过 id(a) == id(b)
证实。且因为两者占用同样的内存空间,修改其中一个,另一个也必然随之变化。
浅拷贝会拷贝父对象本身,但不会拷贝父对象内的子对象,举例来说:
c = a.copy()
id(a), id(c)
"""
>> (3121395149440, 3121395270528)
"""
a, c
"""
>> ({'Roma': ['I', 'V', 'X']}, {'Roma': ['I', 'V', 'X']})
"""
c['Arab'] = [1]
a, c
"""
>> ({'Roma': ['I', 'V', 'X']},
>> {'Roma': ['I', 'V', 'X'], 'Arab': [1]})
"""
c['Roma'].append('C')
a, c
"""
>> ({'Roma': ['I', 'V', 'X', 'C']},
>> {'Roma': ['I', 'V', 'X', 'C'], 'Arab': [1]})
"""
也就是说,我们可以把浅拷贝理解为只拷贝了一层,即父对象。此时,通过 id()
我们也可以发现c 和 a 已经不再是同一个对象了。修改 c 将不会对 a 产生影响。如上,我们在 c 中新增了一个键值对元素 c['Arab'] = [1]
,但 a 并未发生变化。
但是,如果我们对 c 中原本也属于 a 的子对象做修改,a 也会发生相应的变化。a 和 c 此时虽然是独立的对象,但他们的子对象还是指向统一对象(是引用),如下图所示:
我们也可以通过 id
来验证以上想法:
for key, value in a.items():
print(id(value)
"""
>> 3121395686848
"""
for key, value in c.items():
print(id(value)
"""
>> 3121395686848
>> 3121395163328
"""
可以看到,a 和 c 中的 [‘I’, ‘V’, ‘X’, ‘C’] 都指向同一块内存。
此外,我们也可以通过 c = a[:]
或者 c = [ch for ch in a]
来对 a 进行浅拷贝操作。
深度拷贝相当于完全拷贝了父对象及其子对象。此时两个对象完全是两个互不关联的对象,对任何其中做任何修改都不会改变另一个。
import copy
d = copy.deepcopy(a)
a['Arab'] = [1, 2, 3]
a['Roma'].append('M')
d['Roma'].append('D')
a, d
"""
>> ({'Roma': ['I', 'V', 'X', 'C', 'M'], 'Arab': [1, 2, 3]},
>> {'Roma': ['I', 'V', 'X', 'C', 'D']})
"""
上图中 {'Arab': }
未画出。
需要注意的是,拷贝操作只针对一些容器数据类型,例如列表、字典等,对于数字、字符等数据类型,都是直接赋值,即对原对象的引用。
在算法题中,我们经常被要求原地对数组或者列表进行操作,我们将这里的原地理解为在算法运行结束之后,仍然会提取原来变量所在的那块内存空间的值(不考虑空间复杂度的影响)。举个例子:
matrix = [1, 2, 3]
id(matrix)
"""
>> 3121410813888
"""
new_matrix = [4, 6, 7]
matrix[:] = new_matrix
id(matrix)
"""
3121410813888
"""
id(new_matrix)
"""
3121411906752
"""
我们需要对 数组 matrix
原地操作,此时赋值语句的形式就必须写成浅拷贝的形式,在拷贝完成之和,我们可以看到 matrix
仍然存储在相同的内存空间。
然而,如果我们直接调用 matrix = new_matrix
,那么新的 matrix
值将会被存储在新的内存空间,也就是对 new_matrix
的引用。对于原地操作来说,相当于 matrix
的值并未改变。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。