24

任何熟悉 Python 内部结构(CPython 或其他实现)的人都可以解释为什么列表添加必须是同质的:

In [1]: x = [1]

In [2]: x+"foo"
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
C:\Users\Marcin\<ipython-input-2-94cd84126ddc> in <module>()
----> 1 x+"foo"

TypeError: can only concatenate list (not "str") to list

In [3]: x+="foo"

In [4]: x
Out[4]: [1, 'f', 'o', 'o']

为什么上述内容不应该返回与上述成绩单中x+"foo"的最终值相同的值?x

这个问题来自 NPE 的问题:Python's list += iterable 的行为是否记录在任何地方?

更新:我知道不需要异构+=工作(但确实如此),同样,不需要异构工作+是错误的。这个问题是关于为什么做出后一种选择。

说将序列添加到列表中的结果是不确定的,这太过分了。如果这是一个充分的反对意见,那么防止异质+=. 更新 2:特别是,python 总是将运算符调用委托给左侧操作数,因此不会出现“什么是正确的事情”的问题:左侧对象总是管理(除非它委托给右侧)。

更新 3:对于任何争论这是一个设计决定的人,请解释 (a) 为什么它没有记录在案;(b) 记录在案的地方。

Update4:“应该[1] + (2, )返回什么?” 它应该返回一个结果值,该值等于x最初保持[1]在 之后的变量的值x+=(2, )。这个结果是明确定义的。

4

4 回答 4

11

来自 Python 之禅:

面对模棱两可,拒绝猜测的诱惑。

让我们看看这里发生了什么:

x + y

这给了我们一个值,但是是什么类型的呢?当我们在现实生活中添加东西时,我们期望类型与输入类型相同,但如果它们是不同的呢?好吧,在现实世界中,我们拒绝添加1and "a",这没有任何意义。

如果我们有类似的类型怎么办?在现实世界中,我们看上下文。计算机无法做到这一点,所以它必须猜测。Python 选择左操作数并让其决定。您的问题是由于缺乏上下文而发生的。

假设程序员想要做["a"] + "bc"- 这可能意味着他们想要"abc"["a", "b", "c"]. 目前,解决方案是调用"".join()第一个操作数或list()第二个操作数,这允许程序员做他们想做的事情,并且清晰明确。

您的建议是让 Python 猜测(通过内置规则来选择给定的操作数),因此程序员可以通过加法来做同样的事情 - 为什么这样更好?这只是意味着更容易错误地得到错误的类型,我们必须记住一个任意规则(左操作数选择类型)。相反,我们得到一个错误,因此我们可以为 Python 提供进行正确调用所需的信息。

那么为什么不+=一样呢?嗯,那是因为我们给了 Python 那个上下文。通过就地操作,我们告诉 Python 修改一个值,所以我们知道我们正在处理我们正在修改的值的类型。这是 Python 进行正确调用所需的上下文,因此我们无需猜测。

当我谈论猜测时,我说的是 Python 猜测程序员的意图。这是 Python 经常做的事情——参见 3.x 中的除法。/做浮点除法,纠正了它在 2.x 中是整数除法的错误。

这是因为当我们尝试除法时,我们隐含地要求浮点除法。Python考虑到了这一点,它的操作就是按照这个来完成的。同样,这里是关于猜测意图。当我们添加+我们的意图是不清楚的。我们用+=的时候,就很清楚了。

于 2012-12-16T20:53:52.693 回答
9

这些错误报告表明这种设计怪癖是一个错误。

问题12318

是的,这是预期的行为,是的,它是不一致的。

这种情况已经有很长一段时间了,Guido 说他不会再这样做了(这在他的遗憾清单中)。但是,我们不会通过更改代码来破坏代码(list.__iadd__工作方式类似list.extend)。

问题575536

意图是list.__iadd__完全对应于 list.extend(). 也没有必要 list.__add__()过度概括:不想对类似 Martin 的示例感到惊讶的人可以通过使用 plain +for 列表来避免它们。

(当然,我们当中有些人觉得这种行为非常令人惊讶,包括打开该错误报告的开发人员)。

(感谢@Mouad 找到这些)。

于 2012-12-17T13:50:22.977 回答
1

我相信 Python 设计人员以这种方式进行了加法运算,以便 '+' 运算符在结果类型方面保持一致的可交换性:type(a + b) == type(b + a)

每个人都希望 1 + 2 与 2 + 1 有相同的结果。你会希望 [1] + 'foo' 与 'foo' + [1] 相同吗?如果是,结果应该是什么?

您有 3 个选择,您可以选择左操作数作为结果类型,选择右操作数作为结果类型,或者引发错误。

+= 不可交换,因为它包含赋值。在这种情况下,您要么选择左操作数作为结果类型,要么抛出。这里的惊喜a += b是不一样a = a + ba += b不会用英语翻译成“将 a 添加到 b 并将结果分配给 a”。它翻译为“将a添加到b”。这就是为什么它不适用于字符串或元组等不可变对象的原因。

感谢您的评论。编辑了帖子。

于 2012-12-17T03:30:00.707 回答
0

我的猜测是 Python 是强类型的,并且没有明确的指示在这里做正确的事情。您是在要求 Python 附加字符串本身,还是将字符串转换为列表(这是您表示希望它执行的操作)?

请记住,显式优于隐式。在最常见的情况下,这些猜测都不是正确的,并且您不小心尝试做一些您不打算做的事情。提出一个 TypeError 并让你解决它是最安全、最 Pythonic 的事情。

于 2012-12-16T20:24:10.627 回答