Python:a + = b与a = a + b不同

Li *_*oyi 36 python

可能重复:
Python中的加号等于(+ =)做什么?

今天我发现了python语言的一个有趣的"特性",这给了我很多悲伤.

>>> a = [1, 2, 3]
>>> b = "lol"
>>> a = a + b 
TypeError: can only concatenate list (not "str") to list
>>> a += b
>>> a
[1, 2, 3, 'l', 'o', 'l']
Run Code Online (Sandbox Code Playgroud)

那个怎么样?我以为这两个本来是相同的!更糟糕的是,这是我有一段时间调试的代码

>>> a = [1, 2, 3]
>>> b = {'omg': 'noob', 'wtf' : 'bbq'}
>>> a = a + b
TypeError: can only concatenate list (not "dict") to list
>>> a += b
>>> a
[1, 2, 3, 'omg', 'wtf']
Run Code Online (Sandbox Code Playgroud)

WTF!我在我的代码中有列表和dicts,并且想知道我到底是怎么把我的dict的键附加到列表而没有调用.keys().事实证明,这是怎么回事.

我认为这两个陈述是等同的.即使忽略这一点,我也可以理解你将字符串附加到列表上的方式(因为字符串只是字符数组)但是字典?也许如果它附加了一个(键,值)元组列表,但只抓住添加到列表中的键似乎是完全随意的.

有谁知道这背后的逻辑?

Die*_*Epp 37

这一直是一直存在的问题,特别是运算符重载.C++并不是更好.

表达a + b计算从绑定到对象的新列表ab,这不会被修改.将此值重新分配给时a,可以更改一个变量的绑定以指向新值.预计它+是对称的,因此您无法添加词典和列表.

该语句a += b修改绑定到的现有列表a.由于它不会更改对象标识,因此对所表示的对象的所有绑定都可以看到更改a.运算符+=显然不是对称的,它等价于list.extend迭代第二个操作数.对于词典,这意味着列出键.

讨论:

如果某个对象没有实现+=,那么Python会使用+和将其转换为等效语句=.所以这两者有时是等价的,这取决于所涉及的对象的类型.

的好处+=在于变异的referand(相对于操作数的值,这是一个参考)是该实施方式可以是在不实施复杂性也相应增加更加有效.

在其他语言中,您可以使用更明显的表示法.例如,在没有运算符重载的Python的假设版本中,您可能会看到:

a = concat(a, b)
Run Code Online (Sandbox Code Playgroud)

a.extend(a, b)
Run Code Online (Sandbox Code Playgroud)

操作符号实际上只是这些的简写.

奖金:

尝试与其他iterables.

>>> a = [1,2,3]
>>> b = "abc"
>>> a + b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "str") to list
>>> a += b
>>> a
[1, 2, 3, 'a', 'b', 'c']
Run Code Online (Sandbox Code Playgroud)

能够这样做很有用,因为您可以将生成器附加到列表中+=并获取生成器内容.不幸的是它破坏了兼容性+,但是很好.

  • 这是另一个相关的[陷阱](http://zephyrfalcon.org/labs/python_pitfalls.html):`t =([],); t [0] + = [2,3]`.第二个语句会引发一个异常,但之后,`t`就是`([2,3],)`即便如此. (6认同)
  • 我不知道你对"其他可迭代"的意思,这是他问题中的第一个例子吗? (2认同)

Gar*_*Jax 6

这背后的原因是因为python列表(a在你的情况下)实现了__iadd__方法,而__iter__方法又在传递的参数上调用方法.

以下代码段更好地说明了这一点:

class MyDict(dict):
    def __iter__(self):
        print "__iter__ was called"
        return super(MyDict, self).__iter__()


class MyList(list):
    def __iadd__(self, other):
        print "__iadd__ was called"
        return super(MyList, self).__iadd__(other)


a = MyList(['a', 'b', 'c'])
b = MyDict((('d1', 1), ('d2', 2), ('d3', 3)))

a += b

print a
Run Code Online (Sandbox Code Playgroud)

结果是:

__iadd__ was called
__iter__ was called
['a', 'b', 'c', 'd2', 'd3', 'd1']
Run Code Online (Sandbox Code Playgroud)

python解释器检查一个对象是否实现了__iadd__operation(+=),并且只有在它不执行时才会通过执行add操作然后执行赋值来模拟它.