为什么创建列表列表会产生意外行为? [英] Why does creating a list of lists produce unexpected behavior?

查看:98
本文介绍了为什么创建列表列表会产生意外行为?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题是关于为什么是行为的本质,​​而不是如何解决问题的方法,即所谓的行为重复是关于.

This question is about why the behavior is what it is, not how to get around it, which is what the alleged duplicate is about.

在不同情况下,我使用以下表示法来创建一定大小的列表.例如:

I've used the following notation to create lists of a certain size in different cases. For example:

>>> [None] * 5
[None, None, None, None, None]
>>>

这似乎可以正常工作,并且比以下时间短:

This appears to work as expected and is shorter than:

>>> [None for _ in range(5)]
[None, None, None, None, None]
>>>

然后我尝试使用相同的方法创建列表列表:

I then tried to create an list of lists using the same approach:

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

足够公平.它似乎按预期工作.

Fair enough. It seems to work as expected.

但是,在调试器中,我注意到 all 子列表存储桶具有相同的值,即使我仅添加了单个项目.例如:

However, while going through the debugger, I noticed that all the sub-list buckets had the same value, even though I had added only a single item. For example:

>>> t = [[]] * 5
>>> t
[[], [], [], [], []]
>>> t[1].append(4)
>>> t
[[4], [4], [4], [4], [4]]
>>> t[0] is t[1]
True
>>>

我不希望所有顶级数组元素都引用一个 single 子列表.我期望有5个独立子列表.

I was not expecting all top-level array elements to be references to a single sub-list; I expected 5 independent sub-lists.

为此,我必须编写如下代码:

For that, I had to write code like so:

>>> t = [[] for _ in range(5)]
>>> t
[[], [], [], [], []]
>>> t[2].append(4)
>>> t
[[], [], [4], [], []]
>>> t[0] is t[1]
False
>>>

我显然错过了一些东西,可能是历史事实,或者只是一种不同的方式来查看此处的一致性.

I'm clearly missing something, probably a historical fact or simply a different way in which the consistency here is viewed.

有人能解释为什么两个不同的代码片段,它们可以合理地期望彼此等效,但实际上最终隐含地产生了不同且非显而易见的(IMO)结果,特别是考虑到Python的禅宗总是是显性明显?

Can someone explain why two different code snippets that one would reasonably expect to be equivalent to each other actually end up implicitly producing different and non-obvious (IMO) results, especially given Python's zen of always being explicit and obvious?

请注意,我已经知道了这个问题,这与我的要求不同.

Please note that I'm already aware of this question, which is different to what I'm asking.

我只是在寻找详细的说明/理由.如果有此行为的历史,技术和/或理论原因,请确保提供一两个参考.

I'm simply looking for a detailed explanation/justification. If there're historical, technical, and/or theoretical reasons for this behavior, then please be sure to include a reference or two.

推荐答案

执行以下操作:

[[]]*n

您首先要创建一个列表,然后将*运算符与int n结合使用.这将获取列表中的所有对象,并对其进行n次重复.

You are first creating a list, then using the * operator with an int n. This takes whatever objects are in your list, and creates n- many repetitions of it.

但是由于在Python中,显式比隐式好,所以您不必隐式地复制这些对象.确实,这与Python的语义是一致的.

But since in Python, explicit is better than implicit, you don't implicitly make a copy of those objects. Indeed, this is consistent with the semantics of Python.

尝试命名一个示例,其中Python 隐式进行复制.

Try to name a single case where Python implicitly makes a copy.

此外,它与列表中的添加内容一致:

Furthermore, it is consistent with the addition on the list:

l = [1, [], 'a']

l2 = l + l + l

l[1].append('foo')

print(l2)

输出:

[1, ['foo'], 'a', 1, ['foo'], 'a', 1, ['foo'], 'a']

现在,正如注释中指出的那样,来自C ++的上述含义将是令人惊讶的,但是如果将其用于Python,则以上就是期望.

Now, as noted in the comments, coming from C++ it makes sense that the above would be surprising, but if one is used to Python, the above is what one would expect.

另一方面:

[[] for _ in range(5)]

是列表理解.等效于:

lst = []
for _ in range(5):
    lst.append([])

很明显,每次您在循环中时,都会创建一个新列表.这就是文字语法的工作原理.

Here, clearly, every time you are in the loop you create a new list. That is how literal syntax works.

顺便说一句,除了我喜欢的一个特定习惯用法外,我几乎从不在列表上使用*运算符:

As an aside, I almost never use the * operator on lists, except for one particular idiom I am fond of:

>>> x = list(range(1, 22))
>>> it_by_three = [iter(x)]*3
>>> for a,b,c in zip(*it_by_three):
...    print(a, b, c)
...
1 2 3
4 5 6
7 8 9
10 11 12
13 14 15
16 17 18
19 20 21

这篇关于为什么创建列表列表会产生意外行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆