Generating sublists using multiplication ( * ) unexpected behavior
我肯定有人回答了这个问题,但我不知道该如何描述。
假设我想创建一个包含3个空列表的列表,如下所示:
1 | lst = [[], [], []] |
我觉得我做这件事很聪明:
1 | lst = [[]] * 3 |
但是我发现,在调试了一些奇怪的行为之后,这导致了一个子列表的追加更新,比如说
但是,如果我用
1 | lst = [[] for i in range(3)] |
那么做
我的问题是为什么会这样?有趣的是,如果我这样做的话
1 2 3 | lst = [[]]*3 lst[0] = [5] lst[0].append(3) |
然后,细胞0的"连接"断了,我得到了
我的最佳猜测是,使用
My best guess is that using multiplication in the form
[[]] * x causes Python to store a reference to a single cell...?
对。你可以自己测试这个
1 2 3 | >>> lst = [[]] * 3 >>> print [id(x) for x in lst] [11124864, 11124864, 11124864] |
这表明所有三个引用都指向同一个对象。请注意,这件事真的很有道理。它只复制值,在本例中,值是引用。这就是为什么你看到同一个参考重复三次的原因。
It is interesting to note that if I do
1 2 3 | lst = [[]]*3 lst[0] = [5] lst[0].append(3) |
then the 'linkage' of cell 0 is broken and I get
[[5,3],[],[]] , butlst[1].append(0) still causes[[5,3],[0],[0] .
您更改了占用
这是人们在使用指针和引用时犯的一个典型错误。这是一个简单的类比。你有一张纸。在上面写下某人家的地址。你现在拿着那张纸,复印两次,这样你就得到了三张写着同样地址的纸。现在,拿第一张纸,草草写下写在上面的地址,然后给别人的房子写一个新地址。另外两张纸上的地址有变化吗?不,不过这正是你的代码所做的。这就是其他两项不变的原因。再者,想象一下,地址还在第二张纸上的房子的主人为他们的房子建造了一个附加车库。现在我问你,地址在第三张纸上的房子有附加车库吗?是的,是的,因为它和写在第二张纸上的地址完全一样。这解释了关于第二个代码示例的所有内容。
1:你没想到python会调用一个"复制构造函数",对吧?呕吐。
它们引用的是相同的列表。
这里也有类似的问题
从常见问题解答:
" * doesn’t create copies, it only creates references to the existing
objects."
这是因为序列乘法只重复引用。当您编写
1 2 3 4 5 6 | >>> l1 = [[]] * 2 >>> l2 = [[] for _ in xrange(2)] >>> l1[0] is l1[1] True >>> l2[0] is l2[1] False |
基本上,在第一个示例中发生的事情是,正在创建一个列表,其中包含对同一内部列表的多个引用。这是故障报告。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | >>> a = [] >>> b = [a] >>> c = b * 3 # c now contains three references to a >>> d = [ a for _ in xrange(4) ] # and d contains four references to a >>> print c [[], [], []] >>> print d [[], [], [], []] >>> a.append(3) >>> print c [[3], [3], [3]] >>> print d [[3], [3], [3], [3]] >>> x = [[]] * 3 # shorthand equivalent to c >>> print x [[], [], []] >>> x[0].append(3) >>> print x [[3], [3], [3]] |
上面的例子相当于第一个例子。既然每个列表都有自己的变量,希望能更清楚地说明原因。
第二个示例创建多个不同的内部列表对象。
1 2 3 4 5 6 7 8 | >>> c = [[], [], []] # this line creates four different lists >>> d = [ [] for _ in xrange(3) ] # so does this line >>> c[0].append(4) >>> d[0].append(5) >>> print c [[4], [], []] >>> print d [[5], [], []] |
您认为使用形式为[[]*x的乘法会导致python存储对单个单元格的引用是正确的。
因此,您最终得到了对同一列表的3个引用的列表。