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