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】:在您的第一个示例中,K
和 L
变量名称引用同一个对象,因此当您调用改变该对象的方法时,显然可以通过两个引用看到更改。在第二个示例中,+
运算符调用 list.__add__
,它返回 新对象(两个列表的连接),L
名称现在引用这个新对象,而 K
完好无损。
【讨论】:
那么,当我将一个变量分配给一个列表时,它会获得对该对象的引用吗?这是因为实际列表不是列表而是参考?我想我现在明白了。列表和字典可能会发生这种情况,对吗?因为它们实际上都是对对象本身的引用。非常感谢您的澄清。我在别处读到的东西只是含糊不清,并没有真正说使用 + 运算符调用了列表。__add__ 谢谢(^_^) 对 += 有什么想法吗? 不仅list
和dict
,Python中的everything都是一个对象。所有对象都存储在堆中,所有变量都只是引用。但是有些对象是不可变的(比如int
s 和string
s),有些不可变对象是被实习的,所以你不会得到这种令人费解的行为。您必须区分修改数据和修改变量名和数据之间的映射。后者是当您将某些内容分配给变量时发生的情况。但是请注意,如果变量映射表示对象的成员,则它本身可以是数据的一部分。
Re +=
运算符:这只是调用修改对象的__iadd__
方法的语法糖。
谢谢,所有这些都非常有帮助。【参考方案4】:
使用append
,您可以直接修改列表。使用L=L+[4]
,您将复制原始L
并添加一个新元素,然后将该结果分配回L
并打破其与K
的等价性。
我不确定+=
的行为。
【讨论】:
+=
似乎表现得像append
(我刚刚测试过)
@Andre,不。它的行为类似于extend
。以上是关于LIST.append(1) 和 LIST = LIST + [1] (Python) 有啥区别的主要内容,如果未能解决你的问题,请参考以下文章
python之list.append和list.extend的区别