Python list.append 输出值不同于 list.extend

Python list.append output values differ from list.extend

在另一个网站上看到一个关于一段 Python 代码的问题,这个问题让一些人抓狂。这是一段相当小、看起来很简单的代码,所以我查看了它,弄清楚它试图做什么,然后 运行 它在我的本地系统上,并发现了为什么它让最初的提问者发疯.希望这里有人可以帮助我了解发生了什么。

该代码似乎是一个简单的 "ask the user for three values (x,y,z) and a sum (n); iterate all values to find tuples that sum to n, and add those tuples to a list." 解决方案。但它输出的不是所有总和为 n 的元组,而是一个元组列表,其计数等于总和为 n 的元组的计数,但其内容都是“[x,y,z] ”。为了解决这个问题,我将 append 调用更改为 extend 调用(知道这会取消列出添加的元组),以查看行为是否完全改变。我希望得到相同的输出,就像 "x,y,z,x,y,z..." 重复,而不是重复“[x,y,z],[x,y,z]”,因为当我阅读和理解 Python文档,这就是在列表上追加和扩展之间的区别。当我使用 extend 时,我得到的是总和为 n 的元组的正确值,只是通过 extend 打破了它们的元组形式。

问题代码如下:

my = []

x = 3
y = 5
z = 7
n = 11

part = [0,0,0]
for i in range(x+1):
    part[0] = i
    for j in range(y+1):
        part[1] = j
        for k in range(z+1):
            part[2] = k
            if sum(part) == n:
                my.append(part)
print(my)

和输出:

[[3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7]]

这是扩展输出:

[0, 4, 7, 0, 5, 6, 1, 3, 7, 1, 4, 6, 1, 5, 5, 2, 2, 7, 2, 3, 6, 2, 4, 5, 2, 5, 4, 3, 1, 7, 3, 2, 6, 3, 3, 5, 3, 4, 4, 3, 5, 3]

以及扩展代码:

my = []

x = 3
y = 5
z = 7
n = 11

part = [0,0,0]
for i in range(x+1):
    part[0] = i
    for j in range(y+1):
        part[1] = j
        for k in range(z+1):
            part[2] = k
            if sum(part) == n:
                my.extend(part)
print(my)

如果能对此有所启发,我们将不胜感激。我在 Google 和几个问答网站上搜索了一段时间,我发现的关于 Python append/extend 增量的唯一内容似乎与这个问题。

{编辑:环境细节}

此外,运行 这在 Python 2.7.10 和 Python 3.4.3(cygwin,在 Windows 10 主页下)都具有相同的结果。

extend 将参数列表中的项目添加到进行调用的列表对象中。更像是,将对象从一个列表转储到另一个列表而不清空前者。

append 另一方面,只是 appends;而已。因此,将一个列表对象附加到另一个列表,并使用对附加列表的现有引用可能会造成一些损害 - 如本例所示。附加列表后,part 仍然保留对列表的引用(因为您正在修改 到位 ),所以您实际上是在修改和(重新)每次附加相同的列表对象。

您可以通过在 append 案例的每个 parent 迭代开始时构建一个新列表来防止这种情况。

或者简单地附加一份 part 列表:

my.append(part[:]) 
my.append(list(part))
my.append(part.copy())  # Python 3 only

这将附加一个列表,该列表在其新父列表之外没有其他现有引用。

发生了几件事 - 附加和扩展之间的区别,以及列表的可变性。

考虑一个更简单的案例:

In [320]: part=[0,0,0]
In [321]: alist=[]
In [322]: alist.append(part)
In [323]: alist
Out[323]: [[0, 0, 0]]  

append 实际上在列表中放置了指向 part 的指针。

In [324]: alist.extend(part)
In [325]: alist
Out[325]: [[0, 0, 0], 0, 0, 0]

extendpart 的元素放入列表中,而不是 part 本身。

如果我们更改 part 中的一个元素,我们可以看到这种差异的后果:

In [326]: part[1]=1
In [327]: alist
Out[327]: [[0, 1, 0], 0, 0, 0]

追加part也变了,但扩展部分没变

这就是为什么您的 append 案例由子列表组成,并且子列表的最终值都是 part - 因为它们都是 part.

extendpart 的当前值放入列表中。它们不仅不是子列表,而且不会随着 part 的变化而变化。

这是列表指针问题的变体:

In [333]: alist = [part]*3
In [334]: alist
Out[334]: [[0, 1, 0], [0, 1, 0], [0, 1, 0]]
In [335]: alist[0][0]=2
In [336]: part
Out[336]: [2, 1, 0]
In [337]: alist
Out[337]: [[2, 1, 0], [2, 1, 0], [2, 1, 0]]

alist 包含 3 个指向 part 的指针(不是 3 个副本)。更改其中一个子列表,我们将更改所有子列表,包括 part.