文章来自《Python cookbook》.

翻译仅仅是为了个人学习,其它商业版权纠纷与此无关!

-- Zoom.Quiet [2004-08-11 01:40:28]

1. 不用共享引用建立列表的列表

.18 Creating Lists of Lists Without Sharing References 不用共享引用建立列表的列表

Credit: David Ascher

1.1. 问题 Problem

You want to create a multidimensional list, but the apparently simplest solution is fraught with surprises.

你想建立一个多维list, 但是最简单的方法充满惊奇

1.2. 解决 Solution

Use list comprehensions (also known as list displays) to avoid implicit reference sharing:

使用list内涵(也叫做list display)避免隐式的引用共享

multilist = [[0 for col in range(5)] for row in range(10)] 

1.3. 讨论 Discussion

When a newcomer to Python is shown the power of the multiplication operation on lists, he often gets quite excited about it, since it is such an elegant notation. For example:

当在list上显示乘法操作给一个python新手时,他经常为这个相当的激动。因为它是那样的优雅自然。例如:

>>> [0] * 5 
[0, 0, 0, 0, 0] 

The problem is that one-dimensional problems often grow a second dimension, so there is a natural progression to:

问题是一个一维问题经常会增长成为二维。所以,有一个自然的过程:

>>> multi = [[0] * 5] * 3 
>>> print multi 
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]] 

This appears to have worked, but the same newcomer is then often puzzled by bugs, which typically can be boiled down to the following test:

这个看起来可以工作,但是同样的新手经常被bug迷惑。它典型的能够被归结为下面的测试:

>>> multi[0][0] = 'Changed!' 
>>> print multi 
[['Changed!', 0, 0, 0, 0], ['Changed!', 0, 0, 0, 0], ['Changed!', 0, 0, 0, 0]] 

This problem definitely confuses most programmers at least once, if not a few times (see the FAQ entry at http://www.python.org/doc/FAQ.html#4.50). To understand it, it helps to decompose the creation of the multidimensional list into two steps:

这个问题如果不是有很多次 ,但至少有一次会明显的使许多程序员糊涂。为了理解它,分解多维list的创建到两个步骤:

>>> row = [0] * 5 # a list with five references to 0 
>>> multi = [row] * 3 # a list with three references to 
the row object 

The problem still exists in this version (Python is not that magical). The comments are key to understanding the source of the confusion. The process of multiplying a sequence by a number creates a new sequence with the specified number of new references to the original contents. In the case of the creation of row, it doesn't matter whether references are being duplicated or not, since the referent (the object being referred to) is immutable. In other words, there is no difference between an object and a reference to an object if that object is immutable. In the second line, however, what is created is a new list containing three references to the contents of the [row] list, which is a single reference to a list. Thus, multi contains three references to a single object. So when the first element of the first element of multi is changed, you are actually modifying the first element of the shared list. Hence the surprise.

问题仍然存在在这个版本里(python不是那样不可思议的)。 注解是理解混乱根源的关键。一个序列乘一个数字的过程建立了一个新的序列,这个序列有指定数目的新的带有原始内容的引用。在建造行的情况中, 它不管是否引用正在被复制与否,因为引用(被引用的对象)是不可变的。换句话说,如果一个对象是不可变的,那么在一个对象和这个对象引用之间没有什么不同。 然而,在第二行,所构造的是一个新的包含三个[row]列表内容的引用。 它不是一个list的单独的引用。这样,多重list包含了三个引用到一个独立对象。 所以,当多维的第一个元素的第一个元素被改变。你正在真正改变了共享list的第一个元素。从此,事情令人惊讶

List comprehensions, added in Python 2.2, provide a nice syntax that avoids the problem, as illustrated in the solution. With list comprehensions, there is no sharing of references梚t's a truly nested computation. Note that the performance characteristics of the solution are O(M x N), meaning that it will scale with each dimension. The list-multiplication idiom, however, is an O(M) computation, as it doesn't really do duplications.

List内涵,增加在python2.2,提供了一个很好的语法来避免这个问题,就象在解决方案里描述的那样。使用list内涵,没有共享引用。那是一个真正的嵌套运算。注意,解决方案的性能特征是O(M x N)。它意味着同维数成比例。 然而,List乘法惯用法,是一个O(M)运算。它不真正的做复制。

1.4. 参考 See Also

Documentation for the range built-in function in the Library Reference.