LIST.append(1) 和 LIST = LIST + [1] (Python) 有啥区别

Posted

技术标签:

【中文标题】LIST.append(1) 和 LIST = LIST + [1] (Python) 有啥区别【英文标题】:What is the difference between LIST.append(1) and LIST = LIST + [1] (Python)LIST.append(1) 和 LIST = LIST + [1] (Python) 有什么区别 【发布时间】:2011-04-07 23:58:06 【问题描述】:

当我执行(我正在使用交互式 shell)这些语句时,我得到了这个:

L=[1,2,3]
K=L

L.append(4)

L
[1,2,3,4]
K
[1,2,3,4]

但是当我做同样的事情时,将 L.append(4) 替换为 L=L+[4] 我明白了:

L
[1,2,3,4]
K
[1,2,3]

这是某种参考吗?为什么会这样?

我注意到的另一个有趣的事情是 L+=[4] 的行为类似于 .append,这很奇怪,因为我认为它的行为类似于 L = L + [4]。

我们将不胜感激。

谢谢

【问题讨论】:

+= 在 python 中很奇怪。例如a = (1, 2); a += (2,) 产生(1, 2, 3)!这与 ite 修改列表的列表情况完全相反。无法就地修改元组。这就是为什么很多人更喜欢总是使用a = a + b 的形式。 不,它没有,在 a = (1, 2); 之后a += (2,) a 是 (1,2,2) 这有什么奇怪的? 我认为他的意思是a += (3,)。它允许您像这样修改(不可变)元组,这有点奇怪。 @Kugel,正确。这是我的一个错字。奇怪的是它适用于列表,但不适用于元组。 += 运算符没有统一的行为。在某些情况下,它等同于a = a + b,而在其他情况下(列表),它完全是一个不同的操作。在大多数语言中,它总是等价于a = a + b 这很有意义,+= 不能在每个对象类型上都是统一的。这里一致且重要的是不可变对象不会被修改而可变对象会被修改。 【参考方案1】:

如果您对字节码感兴趣:

>>> def L_app( ):
...     L.append( 4 )
...
>>> def L_add( ):
...     L = L + [ 4 ]
...
>>> def L_add_inplace( ):
...     L += [ 4 ]
...
>>> dis.dis( L_app )
  2           0 LOAD_GLOBAL              0 (L)
              3 LOAD_ATTR                1 (append)
              6 LOAD_CONST               1 (4)
              9 CALL_FUNCTION            1
             12 POP_TOP
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE
>>> dis.dis( L_add )
  2           0 LOAD_FAST                0 (L)
              3 LOAD_CONST               1 (4)
              6 BUILD_LIST               1
              9 BINARY_ADD
             10 STORE_FAST               0 (L)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE
>>> dis.dis( L_add_inplace )
  2           0 LOAD_FAST                0 (L)
              3 LOAD_CONST               1 (4)
              6 BUILD_LIST               1
              9 INPLACE_ADD
             10 STORE_FAST               0 (L)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE

【讨论】:

最有趣的案例是+= 表单。 虽然对编程并不陌生,但我对 python 还是很陌生。这(字节码)对我来说没有多大意义。字节码通常不是比语言级别低的代码,更接近机器代码并且运行速度更快吗?也许我得到了错误的定义。 @aaronasterling:确实很有趣!添加。 @aitee:是的,他们是。 dis 打印出 Python 函数编译成的字节码的“漂亮”表示。这对于查看“幕后”发生的事情通常很有用。【参考方案2】:
L.append(4)

这会在现有列表L 的末尾添加一个元素。

L += [4]

+= 运算符调用神奇的__iadd__() 方法。事实证明list 覆盖了__iadd__() 方法并使其等效于extend(),它与append() 一样,直接将元素添加到现有列表中。

L = L + [4]

L + [4] 生成一个等于L 的新列表,并在末尾添加了 4。这个列表然后被分配回L。因为您已经创建了一个新的列表对象,所以 K 不会因为这个赋值而改变。

我们可以使用id() 来识别何时创建了新的对象引用:

>>> L = [1, 2, 3]
>>> id(L)
152678284
>>> L.append(4)
>>> id(L)
152678284

>>> L = [1, 2, 3]
>>> id(L)
152680524
>>> L = L + [4]
>>> id(L)
152678316

【讨论】:

【参考方案3】:

在您的第一个示例中,KL 变量名称引用同一个对象,因此当您调用改变该对象的方法时,显然可以通过两个引用看到更改。在第二个示例中,+ 运算符调用 list.__add__,它返回 新对象(两个列表的连接),L 名称现在引用这个新对象,而 K 完好无损。

【讨论】:

那么,当我将一个变量分配给一个列表时,它会获得对该对象的引用吗?这是因为实际列表不是列表而是参考?我想我现在明白了。列表和字典可能会发生这种情况,对吗?因为它们实际上都是对对象本身的引用。非常感谢您的澄清。我在别处读到的东西只是含糊不清,并没有真正说使用 + 运算符调用了列表。__add__ 谢谢(^_^) 对 += 有什么想法吗? 不仅listdict,Python中的everything都是一个对象。所有对象都存储在堆中,所有变量都只是引用。但是有些对象是不可变的(比如ints 和strings),有些不可变对象是被实习的,所以你不会得到这种令人费解的行为。您必须区分修改数据和修改变量名和数据之间的映射。后者是当您将某些内容分配给变量时发生的情况。但是请注意,如果变量映射表示对象的成员,则它本身可以是数据的一部分。 Re += 运算符:这只是调用修改对象的__iadd__ 方法的语法糖。 谢谢,所有这些都非常有帮助。【参考方案4】:

使用append,您可以直接修改列表。使用L=L+[4],您将复制原始L 并添加一个新元素,然后将该结果分配回L 并打破其与K 的等价性。

我不确定+= 的行为。

【讨论】:

+= 似乎表现得像append(我刚刚测试过) @Andre,不。它的行为类似于extend

以上是关于LIST.append(1) 和 LIST = LIST + [1] (Python) 有啥区别的主要内容,如果未能解决你的问题,请参考以下文章

python中append()与extend()方法的区别

Python2入门

二维矩阵转置

python之list.append和list.extend的区别

python list.append() 如何在列表名后引用变量

Python List+Tuple+Dict+Set小结