Python list.append输出值与list.extend

时间:2016-11-06 03:02:48

标签: python list append extend

在另一个网站上看到一个关于一段驱使某人疯狂的Python代码的问题。这是一个相当小的,直截了当的代码片段,所以我看着它,弄清楚它想要做什么,然后在我的本地系统上运行它,并发现它为什么驱动原始的提问者。希望有人能帮助我了解正在发生的事情。

代码似乎是一个简单的“向用户询问三个值(x,y,z)和一个sum(n);迭代所有值以找到总和为n的元组,并将这些元组添加到列表中。 “解。但它输出的是,而不是总和为n的所有元组,一个元组列表,其计数等于总和为n的元组的数量,但其内容都是“[x,y,z] ”。试图绕过这个问题,我将追加调用更改为一个扩展调用(知道这将取消列出添加的元组),以查看行为是否发生了变化。我希望重复获得相同的输出,就像“x,y,z,x,y,z ......”一样,而不是“[x,y,z],[x,y,z]”重复,因为当我阅读并理解Python文档时,这就是在列表上追加和扩展之间的区别。当我使用extend时,我得到的是元组的正确值,它们总和为n,只是通过扩展从元组形式中分解出来。

以下是问题代码:

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)

任何可以解决这个问题的光都会非常感激。我在谷歌和几个Q& A网站上挖了一段时间,我发现的关于Python附加/扩展增量的唯一内容是与这个问题似乎没有关系的东西。

{edit:environment detail}

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

2 个答案:

答案 0 :(得分:1)

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

另一方面,

append只需追加;而已。因此,将列表对象附加到具有对附加列表的现有引用的另一个列表可能会造成一些损害 - 如本例所示。添加列表后,part仍然保留对列表的引用(因为您正在修改到位),因此您实际上已经修改了(并且)每次附加相同的列表对象。

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

或者只需附加part列表的副本:

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

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

答案 1 :(得分:1)

有一些事情正在发生 - 追加和扩展之间的区别,以及列表的可变性。

考虑一个更简单的案例:

int getrevn(int n, int D)

{
//-------method 1-------------------------//

    int revn = 0;
    while (n)
    {
        revn = revn * D + n % D;
        n /= D;
    }
    return revn;

//------method 2-------------------------//

    string s;
    while (n)
    {
        s = char(n % D + '0') + s;
        n /= D;
    }
    int rev = 0;
    for (unsigned int i = s.size() - 1; i >= 0; i--)
        rev = rev * D + s[i] - '0';
    return rev;
}

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] extend的元素放在列表中,而不是part本身。

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

part

附加In [326]: part[1]=1 In [327]: alist Out[327]: [[0, 1, 0], 0, 0, 0] 也发生了变化,但扩展部分没有变化。

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

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

以下是该列表指针问题的变体:

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个指针(不是3个副本)。更改其中一个子列表,我们将其全部更改,包括part