Python赋值运算符与非赋值不同
Posted
技术标签:
【中文标题】Python赋值运算符与非赋值不同【英文标题】:Python assignment operator differs from non assignment 【发布时间】:2020-08-10 21:42:10 【问题描述】:我遇到了这种奇怪的行为,我找不到解释。
MWE:
l = [1]
l += 'a': 2
l
[1, 'a']
l + 'B': 3
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: can only concatenate list (not "dict") to list
基本上,当我+=
python 不会引发错误并将键附加到列表中时,当我只计算+
时,我会得到预期的TypeError
。
注意:这是 Python 3.6.10
【问题讨论】:
查看https://***.com/questions/15376509/when-is-i-x-different-from-i-i-x-in-python 见bugs.python.org/issue9314 - “用迭代器连接列表时结果不一致”。另请注意,+=
等在 PEP 中被称为“增强分配”,PEP 203。
这有点奇怪
我认为@alkasm 的错误链接有一个很好的解释。 When a is mutable, a += b updates it in-place, so there is no ambiguity: the type of a cannot change. When you do a + b, there is no reason to treat a as more deserving than b when selecting the type of the result.
@ChrisDoyle 这就是确切的推理。这不是“错误”,它是一种防止解释器猜测的机制。
【参考方案1】:
l += ...
实际上是在调用object.__iadd__(self, other)
并在l
是可变时修改对象in-place
原因(正如@DeepSpace 在他的评论中解释的那样)是,当您执行 l += 'a': 2
时,操作仅在原地更新 l
并且仅当 l
是可变的时。另一方面,l + 'a': 2
操作未在适当位置完成,导致list + dictionary -> TypeError
。
(见here)
l = [1]
l = l.__iadd__('a': 2)
l
#[1, 'a']
与调用object.__add__(self, other)
的+
不同
l + 'B': 3
TypeError: can only concatenate list (not "dict") to list
【讨论】:
这也应该说明原因了。l += ...
更新 l
就地,所以结果的类型是明确的(= l
的类型)。 l + ...
是模棱两可的,因为它没有到位,所以生成的新对象的类型不清楚(应该是l
的类型还是应该是...
的类型?)
当然!我将此添加到我的答案中。 l += ...
就地运行
@seralouk,您介意分享您使用哪个工具来找出调用了哪个函数吗?检查、pdb 等?
文档:docs.python.org/3/reference/datamodel.html#object.__iadd__ 和一些编程知识,例如字典是可变的。
@DeepSpace 的推理是有道理的,但 __iadd__
不能保证返回相同的对象。见number += 2
。另外,来自文档:这些方法应该尝试就地执行操作(修改自我)并返回结果(可能是但不一定是自我)【参考方案2】:
正如作者所说,这不是错误。当你做a += b
时,就像b
来到a
的家,并以a
喜欢的方式改变它。 作者所说的是,当您执行a + b
时,无法确定哪种风格会被优先考虑。在你执行之前,没有人知道a + b
的结果会去哪里。所以你无法决定它的风格。如果是a
样式,则为[1, 'a']
的样式,如果为b
样式,则为错误。因此无法决定谁将获得优先权。所以我个人不同意这种说法。因为当您使用调用堆栈时,a
的位置比b
高。当有像a + b
这样的表达式时,如果a.__add__
是NotImplemented
(在这种情况下已实现),则首先调用a.__add__(self, other)
。然后你打电话给a.__radd__(self, other)
。这意味着在这种情况下调用other.__add__
b.__add__
。我是根据调用堆栈的位置来说明这一点的,python 社区可能有更重要的理由这样做。
【讨论】:
以上是关于Python赋值运算符与非赋值不同的主要内容,如果未能解决你的问题,请参考以下文章