内置类型的问题——重复增加一个层次的深度 | 第二部分 类型与操作 —— 第 9 章: 元组,文件和其他 |《学习 python:强大的面向对象编程(第 5 版)》| python 技术论坛-380玩彩网官网入口
重复序列就像把它自己添加到自己许多次。然而,当可变序列被嵌套时,效果可能不总是你预期的那样。比如,下面例子中将l重复4次分配给x,而包含l的列表再重复4次后分配给y:
>>> l = [4, 5, 6]
>>> x = l * 4 # like [4, 5, 6] [4, 5, 6] ...
>>> y = [l] * 4 # [l] [l] ... = [l, l,...]
>>> x
[4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6]
>>> y
[[4, 5, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]]
因为l被嵌套在第二次重复中,y最后嵌套了指回分配给l的原来的列表的引用,所以就会受到前面一节中提到的同样的副作用的影响:
>>> l[1] = 0 # impacts y but not x
>>> x
[4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6]
>>> y
[[4, 0, 6], [4, 0, 6], [4, 0, 6], [4, 0, 6]]
这可能看起来是人为的且不实用——直到它在你的代码中不经意地出现!这个问题的解法和前一节中的一样,因为这其实只是创建共享可变对象引用例子的另一种方式——当你不想要共享引用时,就创建拷贝:
>>> l = [4, 5, 6]
>>> y = [list(l)] * 4 # embed a (shared) copy of l
>>> l[1] = 0
>>> y
[[4, 5, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]]
甚至更微妙地,虽然y不再和l共享对象,但它仍然嵌入了指向它的同一个拷贝的四份引用。如果还必须避免这种共享,就要确保每个嵌入的拷贝是独一无二的:
>>> y[0][1] = 99 # all four copies are still the same
>>> y
[[4, 99, 6], [4, 99, 6], [4, 99, 6], [4, 99, 6]]
>>> l = [4, 5, 6]
>>> y = [list(l) for i in range(4)]
>>> y
[[4, 5, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]]
>>> y[0][1] = 99
>>> y
[[4, 99, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]]
如果记得重复、连接、切片都只拷贝它们的操作数对象的顶层,这些例子就会好理解得多。