像+= 这样的“增强”赋值运算符是在2000 年10 月发布的Python 2.0 中引入的。PEP 203 中描述了设计和基本原理。这些运营商宣布的目标之一是支持就地运营。写作
a = [1, 2, 3]
a += [4, 5, 6]
应该更新列表a就地。如果有其他对列表 a 的引用,这很重要,例如当a 作为函数参数接收时。
但是,操作不能总是在原地发生,因为许多 Python 类型,包括整数和字符串,都是不可变的,例如i += 1 用于整数 i 不可能就地操作。
总而言之,增强的赋值运算符应该在可能的情况下就地工作,否则就创建一个新对象。为了实现这些设计目标,表达式x += y 被指定为如下行为:
- 如果定义了
x.__iadd__,则评估x.__iadd__(y)。
- 否则,如果实现了
x.__add__,则评估x.__add__(y)。
- 否则,如果实现了
y.__radd__,则评估y.__radd__(x)。
- 否则会引发错误。
此过程获得的第一个结果将被分配回x(除非该结果是NotImplemented 单例,在这种情况下查找会继续下一步)。
此过程允许支持就地修改的类型实现__iadd__()。 不支持就地修改的类型不需要添加任何新的魔法方法,因为 Python 会自动回退到本质上的 x = x + y。
所以最后让我们来回答您的实际问题——为什么您可以使用增强的赋值运算符将元组添加到列表中。凭记忆,这件事的历史大致是这样的:list.__iadd__() 方法被实现为简单地调用 Python 2.0 中已经存在的list.extend() 方法。在 Python 2.1 中引入迭代器时,list.extend() 方法已更新为接受任意迭代器。这些更改的最终结果是 my_list += my_tuple 从 Python 2.1 开始工作。然而,list.__add__() 方法从来不应该支持任意迭代器作为右手参数——这被认为不适合强类型语言。
我个人认为增强运算符的实现最终在 Python 中有点过于复杂。它有许多令人惊讶的副作用,例如这段代码:
t = ([42], [43])
t[0] += [44]
第二行引发TypeError: 'tuple' object does not support item assignment,but the operation is successfully performed anyway – 在执行引发错误的行后,t 将是([42, 44], [43])。