外观
对象拷贝问题
说明
本节会运用到 import
语句,相关知识将会在第三部分详细介绍。不了解这个语句并不影响理解本节主要内容,但你也可以阅读完第三章后再回来看。
现象分析
先来看一串代码:
>>> a = []
>>> b = a
>>> b.append(1)
>>> a
[1]
>>> b
[1]
这一现象对于阅读过第二章节的朋友来说应该很容易解释:在本例中,a
和 b
实际上指向了同一个列表对象。
这一现象在更深层的嵌套也是存在的:
>>> a = [[1, 2, 3], ['a', 'b', 'c']]
>>> b = a[0]
>>> b.append(4)
>>> a
[[1, 2, 3, 4], ['a', 'b', 'c']]
:::
问题何时存在——可变数据类型
我们先理解一个概念:可变数据类型(Mutable)和不可变数据类型(Immutable)。
举个例子,下面的操作都是合法的:
运行代码
>>> a = [1, 2, 3]
>>> a[0] = 4
>>> b = {'a': 1, 'b': 2}
>>> b['c'] = 3
这些操作都可以在原本的列表和字典对象不变的情况下改变其值。列表、字典和集合属于可变数据类型。通常情况下,所有自定义的类生成的对象也都属于可变数据类型。
然而,字符串和元组属于不可变数据类型。你无法对它们进行更改:
>>> a = '123'
>>> a[0] = '0'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> b = (1, 2, 3)
>>> b[0] = 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
同理属于不可变数据类型的还有 int、float 和 bool,这是显然的,相信没有人会被它们迷惑。
对于可变数据类型,我们大多数的操作都不会生成,。此时,就容易错误地写出本节开头展示的两段代码,在改变一个变量的过程中不小心让不想被改变的数据受到影响。
copy 模块
在某些时候,我们希望为某个对象创建一个“独立”的拷贝。这时 Python 有一个名为 copy
的内置库,可以在一定程度上帮助我们。
copy
>>> import copy
>>> a = [1, 2, 3]
>>> b = copy.copy(a)
>>> b[0] = 0
>>> a
[1, 2, 3]
>>> b
[0, 2, 3]
特别地,对于列表而言,一次切片操作便是对其进行一次部分或全部的浅拷贝:
>>> a = [1, 2, 3]
>>> b = a[:] # 浅拷贝完成
>>> b[0] = 0
>>> a
[1, 2, 3]
>>> b
[0, 2, 3]
deepcopy
copy.copy
只对第一层对象有用(本节的第一个例子),对于对象的嵌套(本节第二个例子)仍不能解决问题:
>>> import copy
>>> a = [[1, 2, 3], ['a', 'b', 'c']]
>>> b = copy.copy(a)
>>> a.append('test')
>>> a[0].append(4)
>>> a
[[1, 2, 3, 4], ['a', 'b', 'c'], 'test']
>>> b
[[1, 2, 3, 4], ['a', 'b', 'c']]
如果想要彻底解决问题,需要使用 copy.deepcopy
>>> import copy
>>> a = [[1, 2, 3], ['a', 'b', 'c']]
>>> b = copy.deepcopy(a)
>>> a.append('test')
>>> a[0].append(4)
>>> a
[[1, 2, 3, 4], ['a', 'b', 'c'], 'test']
>>> b
[[1, 2, 3], ['a', 'b', 'c']]
利用这一特性
对于这一现象,无论你喜不喜欢,你都需要知道这不是特意而为之,而是数据在内存中的行为自然而然形成的逻辑。但是,我们可以利用这一特性。例如,也许你会写出这样的代码:
a = [{'name': 'danny'}, {'name': 'yxzl'}]
for i in range(len(a)):
a[i]['name'] = a[i]['name'].capitalize()
print(a) # [{'name': 'Danny'}, {'name': 'Yxzl'}]
而现在我们知道了,不同变量引用同一个对象时更改是同步的,那么上面的代码就可以改成这样:
a = [{'name': 'danny'}, {'name': 'yxzl'}]
for i in a:
i['name'] = i['name'].capitalize()
print(a) # [{'name': 'Danny'}, {'name': 'Yxzl'}]
版权所有
版权归属:异想之旅