python为什么 a_tuple[i] += [item] 会在执行加法时引发异常?
这是由两个事实共同导致的结果,一是增强赋值运算符属于 赋值 运算符,二是在 Python 中存在可变和不可变两种不同的对象。
此处的讨论在任何对元组中指向可变对象的元素使用增强赋值运算符的情况都是普遍成立的,但在此我们只以 list
和 +=
来举例。
如果你写成这样:
>>> a_tuple = (1, 2)
>>> a_tuple[0] += 1
Traceback (most recent call last):
...
TypeError: "tuple" object does not support item assignment
发生异常的原因是显而易见的: 1
会与对象 a_tuple[0]
相加,而该对象为 (1
),得到结果对象 2
,但当我们试图将运算结果 2
赋值给元组的 0
号元素时就将报错,因为我们不能改变元组的元素所指向的对象。
在表层之处,以上增强赋值语句所做的大致是这样:
>>> result = a_tuple[0] + 1
>>> a_tuple[0] = result
Traceback (most recent call last):
...
TypeError: "tuple" object does not support item assignment
由于元组是不可变的,因此操作的赋值部分会引发错误。
当你这样写的时候:
>>> a_tuple = (["foo"], "bar")
>>> a_tuple[0] += ["item"]
Traceback (most recent call last):
...
TypeError: "tuple" object does not support item assignment
发生异常会令人略感吃惊,还有一个更为令人吃惊的事实:虽然有报错,但是添加操作却生效了:
>>> a_tuple[0]
["foo", "item"]
要明白为何会这样,你需要知道 (a) 如果一个对象实现了 __iadd__
魔术方法,它会在执行 +=
增强赋值时被调用,并且其返回值将用于该赋值语句; (b) 对于列表来说,__iadd__
等价于在列表上调用 extend
并返回该列表。 因此对于列表我们可以说 +=
就是 list.extend
的“快捷方式”:
>>> a_list = []
>>> a_list += [1]
>>> a_list
[1]
这相当于:
>>> result = a_list.__iadd__([1])
>>> a_list = result
a_list 所引用的对象已被修改,而引用被修改对象的指针又重新被赋值给 a_list
。 赋值的最终结果没有变化,因为它是引用 a_list
之前所引用的同一对象的指针,但仍然发生了赋值操作。
因此,在我们的元组示例中,发生的事情等同于:
>>> result = a_tuple[0].__iadd__(["item"])
>>> a_tuple[0] = result
Traceback (most recent call last):
...
TypeError: "tuple" object does not support item assignment
__iadd__
成功执行,因此列表得到了扩充,但是虽然 result
指向了 a_tuple[0]
已经指向的同一对象,最后的赋值仍然导致了报错,因为元组是不可变的。
来源:PY学习网:原文地址:https://www.py.cn/article.html