You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python列表+=运算符实现与参数传递的相关技术疑问

Python列表+=运算符实现与参数传递的相关技术疑问

你提的这两个问题刚好戳中了Python里很多人容易混淆的核心点,咱们一个个说清楚:


问题1:为什么l += [8, 9]和l = l + [8, 9]行为不同?

这背后的核心是运算符的语义差异,和Python的参数传递机制无关——你一开始坚持的「所有参数都是传对象引用」是完全正确的,作者的误解本质是没分清「修改对象本身」和「重新绑定变量名」这两个完全不同的操作。

咱们拆解这两个操作的本质:

  • l = l + [8,9]:这个操作是先创建新对象,再重新绑定变量。具体来说,l + [8,9]会生成一个包含原列表元素和新元素的全新列表,然后l = ...只是让函数里的局部变量l指向这个新列表,完全和最初传入的那个列表对象没关系了,所以外部的y自然不会发生变化。
  • l += [8,9]:这个操作调用的是对象的__iadd__方法(原地加法),对于列表这类可变对象,这个方法的语义是直接修改对象本身,而不是创建新对象。也就是说,它是在原列表的内存空间里直接添加元素,函数里的l始终指向最初传入的那个列表对象,所以外部的y能看到变化。

这里的通用原则可以总结为:

  • 对于支持__iadd__的可变对象,+=是原地修改原对象;
  • 对于所有对象,+都是返回新对象,后续的赋值操作只是重新绑定变量名,和原对象无关。

哪怕不用函数,在全局作用域里跑这两行代码,结果也会呈现同样的差异:

y = [1,2]
y1 = y + [8,9]  # y1是新列表,y仍为[1,2]
y += [8,9]      # y直接被修改为[1,2,8,9]

问题2:+=运算符是不是确实类似extend(self, other)?

你的理解非常准确!对于列表来说,__iadd__的实现逻辑几乎和extend完全一致——它就是在原列表的基础上,把另一个可迭代对象的元素逐个添加进去,不会创建新的列表对象。

如果用Python代码模拟列表的__iadd__方法,逻辑大概是这样的:

def __iadd__(self, other):
    self.extend(other)
    return self  # 注:__iadd__需要返回self,以支持链式操作比如l += [1] += [2]

(实际CPython底层是用C实现的,但核心逻辑和这段Python代码完全相同)

对比来看,不可变对象比如整数的__iadd__实现就不一样了——因为整数无法原地修改,它的__iadd__等价于self + other,也就是返回新的整数对象,这也是为什么i +=1之后外部变量不变的原因:函数里的i被重新绑定到了新的整数对象,和最初传入的x彻底无关了。


最后给你一个能直接说服作者的小例子:通过打印对象内存地址,证明参数传递方式从未改变,只是操作的对象是否为同一个:

def g(l):
    print(id(l))  # 打印原列表的内存地址
    l += [8,9]
    print(id(l))  # 地址和原列表一致,说明始终是同一个对象

def h(l):
    print(id(l))  # 打印原列表的内存地址
    l = l + [8,9]
    print(id(l))  # 地址是新的,说明指向了新创建的列表

运行后你会发现,g函数里两次打印的地址完全相同,h函数里两次地址不同——这直接印证了「不是传递方式不同,而是操作是否修改原对象的差异」。

备注:内容来源于stack exchange,提问作者quizdog

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 13:39:50