i = i + n 真的和 i += n 一样吗? [复制]

Posted

技术标签:

【中文标题】i = i + n 真的和 i += n 一样吗? [复制]【英文标题】:Is i = i + n truly the same as i += n? [duplicate] 【发布时间】:2019-03-15 19:28:26 【问题描述】:

一个代码块可以工作,但另一个不能。这将是有意义的,除了第二个块与第一个块相同,只是用速记编写的操作。它们实际上是相同的操作。

l = ['table']
i = []

版本 1

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

输出:['t', 'a', 'b', 'l', 'e']

版本 2

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

输出:

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


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

【问题讨论】:

不,列表不一样。 += 扩展了一个列表。 + 将两个列表连接成一个新列表。 理想情况下,如果您在代码中使用此想法,则分别使用append()extend() 方法添加元素和连接列表可能更安全,以避免这样的歧义。 【参考方案1】:

它们不必相同。

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

如果您使用x += y,但x 没有提供__iadd__ 方法(或该方法返回NotImplemented),则__add__ 用作fallback,这意味着@ 987654331@ 发生。

在列表的情况下,使用l += iterable 实际上是使用iterable 的元素扩展列表l。在您的情况下,字符串中的每个字符(它是一个可迭代的)都在 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

【讨论】:

【参考方案2】:

没有。

7.2.1. Augmented assignment statements:

x += 1 这样的增强赋值表达式可以重写为x = x + 1 以实现类似但不完全相等的效果。 在增强版本中,x 只计算一次。此外,在可能的情况下, 实际操作是就地执行的,这意味着而不是 创建一个新对象并将其分配给目标,旧对象 改为修改。

【讨论】:

【参考方案3】:

如果在第二种情况下,您在n 周围加上一个列表以避免错误:

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

你得到

['table']

所以它们是不同的操作。

【讨论】:

以上是关于i = i + n 真的和 i += n 一样吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

序列自动机 浅谈

PHP中while循环和for循环的区别?

循环展开和优化

树状数组求后缀和

二分查找真的那么简单吗?——算法第二章上机实践报告

01背包与完全背包