i = i + n是否真的与i + = n相同?

时间:2018-10-10 19:52:05

标签: python python-3.x operator-overloading

一个代码块有效,而另一块无效。除了第二个块与第一个块相同外,这是有意义的,只是第二个块用简写形式进行操作。它们实际上是相同的操作。

for n in l:
    i += n
print(i)

版本1

['t', 'a', 'b', 'l', 'e']

输出:for n in l: i = i + n print(i)

版本2

{{1}}

输出:

  

TypeError:只能将列表(而不是“ str”)连接到列表


是什么导致这个奇怪的错误?

3 个答案:

答案 0 :(得分:79)

它们不必相同。

使用+运算符调用方法__add__,而使用+=运算符调用__iadd__。完全取决于所讨论的对象,当调用这些方法之一时会发生什么。

如果您使用x += yx不提供__iadd__方法(或者该方法返回NotImplemented),则将__add__用作 fallback ,表示发生x = x + y

对于列表,使用l += iterable实际上使用l的元素扩展了列表iterable。对于您而言,在extend操作期间会附加字符串中的每个字符(可迭代)。

演示1:使用__iadd__

>>> l = []
>>> l += 'table'
>>> l
['t', 'a', 'b', 'l', 'e']

演示2:使用extend可以做到

>>> l = []
>>> l.extend('table')
>>> l
['t', 'a', 'b', 'l', 'e']

演示3:添加列表和字符串会引发TypeError

>>> l = []
>>> l = l + 'table'
[...]
TypeError: can only concatenate list (not "str") to list

此处不使用+=会为您提供TypeError,因为只有__iadd__实现了扩展行为。

演示4:常见陷阱:+=不会建立新列表。我们可以通过使用is运算符检查对象是否相等来确认这一点。

>>> l = []
>>> l_ref = l # another name for l, no data is copied here
>>> l += [1, 2, 3] # uses __iadd__, mutates l in-place
>>> l is l_ref # confirm that l and l_ref are names for the same object
True
>>> l
[1, 2, 3]
>>> l_ref # mutations are seen across all names
[1, 2, 3]

但是,l = l + iterable语法确实会建立一个新列表。

>>> l = []
>>> l_ref = l # another name for l, no data is copied here
>>> l = l + [1, 2, 3] # uses __add__, builds new list and reassigns name l
>>> l is l_ref # confirm that l and l_ref are names for different objects
False
>>> l
[1, 2, 3]
>>> l_ref
[]

在某些情况下,这可能会产生细微的错误,因为+= 变异原始列表,而
l = l + iterable建立一个列表,并重新分配名称l

奖励

Ned Batchelder's challenge to find this in the docs

答案 1 :(得分:3)

如果在第二种情况下,您将列表包装在n周围,以避免出现错误:

for n in l:
    i = i + [n]
print(i)

你得到

['table']

所以它们是不同的操作。

答案 2 :(得分:3)

否。

7.2.1. Augmented assignment statements

  

可以将x += 1之类的扩展赋值表达式重写为x = x + 1,以达到相似但不完全相等的效果。在扩展版本中,x仅被评估一次。另外,如果可能,   实际操作是就地执行的,这意味着   创建一个新对象并将其分配给目标(旧对象)   而是修改。