a+=b与a=a+b不同

2024-04-28 19:53:19 发布

您现在位置:Python中文网/ 问答频道 /正文

Possible Duplicate:
Why does += behave unexpectedly on lists?

今天我发现了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']

怎么样?我以为这两个是对等的!更糟糕的是,这是我花了很长时间调试的代码

>>> 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']

世界贸易基金会!我的代码中有列表和dict,我想知道我到底是怎么把dict的键附加到列表中的,而不调用.keys()。事实证明,就是这样。

我以为这两个说法是对等的。即使忽略了这一点,我还是能理解你在列表中附加字符串的方式(因为字符串只是字符数组),但是字典呢?也许如果它附加了一个(键,值)元组列表,但只获取要添加到列表中的键似乎完全是任意的。

有人知道这背后的逻辑吗?


Tags: to字符串代码only列表notcandict
2条回答

这背后的原因是python列表(在您的例子中是a)实现了^{}方法,该方法依次调用传递参数的^{}方法。

下面的代码片段更好地说明了这一点:

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

结果是:

__iadd__ was called
__iter__ was called
['a', 'b', 'c', 'd2', 'd3', 'd1']

python解释器检查一个对象是否实现了__iadd__操作(+=),只有当它没有实现时,它才会通过在赋值之后执行add操作来模拟它。

一般来说,这是而且一直是易变性的问题,尤其是运算符重载。C++没有更好的。

表达式a + b从绑定到未修改的ab的对象中计算新列表。当您将此值赋回a时,您将更改一个变量的绑定以指向新值。预计+是对称的,因此不能添加dict和list。

语句a += b修改绑定到a的现有列表。由于它不会更改对象标识,因此对由a表示的对象的所有绑定都可见这些更改。运算符+=显然不是对称的,它相当于list.extend,它在第二个操作数上迭代。对于字典来说,这意味着列出键。

讨论:

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

使referrand发生变异的+=(与作为引用的操作数值相反)的好处是,在不相应增加实现复杂性的情况下,实现可以更高效。

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

a = concat(a, b)

a.extend(a, b)

运算符符号实际上只是这些的简写。

奖金:

你也可以和其他人一起试试。

>>> 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']

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

相关问题 更多 >