Python中的共享引用是随处可见的,这也就意味着随时注意该行为是非常重要的,否则就可能出错。
假设我们需要创建一个大小为5x5的矩阵并以数字0对矩阵进行初始化,根据Python的一些特点和使用技巧,很可能会这样来做:
>>> matrix = [[0] * 5] * 5 >>> for row in matrix: print row [0, 0, 0, 0, 0] [0, 0, 0, 0, 0] [0, 0, 0, 0, 0] [0, 0, 0, 0, 0] [0, 0, 0, 0, 0]
这看似很有效,不过假如对其中一位赋值的话:
>>> matrix[0][0] = 'hello' >>> for row in matrix: print row ['hello', 0, 0, 0, 0] ['hello', 0, 0, 0, 0] ['hello', 0, 0, 0, 0] ['hello', 0, 0, 0, 0] ['hello', 0, 0, 0, 0]
问题就出现了,那剖析创建矩阵的方法[[0] * 5] * 5,这其中实际分为两个步骤。首先为创建行:
>>> row = [0] * 5 >>> row [0, 0, 0, 0, 0]
也就是说row列表中的5个子项都引用0。之后的操作:
>>> matrix = [row] * 5
这就相当于引用了5次列表row。而问题就出在这里。虽然两次操作都是引用,但内层的引用是数字0,为不可变对象。而外层引用是可变对象列表。即一旦对列表进行修改,那就变成“全局修改”了。
因此这里需要避免的就是避开对列表的引用,保证每行互不引用就可以解决该问题。也就是说,总是去创建列表。
>>> matrix = [[0] * 5 for row in range(5)] >>> for row in matrix: print row ['hello', 0, 0, 0, 0] [0, 0, 0, 0, 0] [0, 0, 0, 0, 0] [0, 0, 0, 0, 0] [0, 0, 0, 0, 0]
所以说理解Python的这些特性是很重要的。