为什么Python列表添加必须是同质的?

时间:2012-12-16 20:04:25

标签: python language-design language-implementation python-internals

任何熟悉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的问题:Is the behaviour of Python's list += iterable documented anywhere?

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

很难说将序列添加到列表中的结果是不确定的。如果这是一个充分的反对意见,那么防止异质+=是有意义的。 Update2:特别是,python总是将操作符调用委托给左边的操作数,所以没有问题出现“什么是正确的事情”:左手对象总是管理(除非它委托给右边)。

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

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

4 个答案:

答案 0 :(得分:11)

来自Python的禅宗:

  

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

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

x + y

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

如果我们有类似的类型怎么办?在现实世界中,我们看一下背景。计算机不能这样做,所以它必须猜测。 Python选择左操作数并让它决定。由于缺乏上下文,您的问题就出现了。

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

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

为什么+=会有所不同?好吧,那是因为我们正在为Python提供上下文。通过就地操作,我们告诉Python修改一个值,所以我们知道我们正在处理我们正在修改的值的类型。这是Python需要进行正确调用的上下文,因此我们不需要猜测。

当我谈论猜测时,我在谈论Python猜测程序员的意图。这是Python做了很多事情 - 请参阅3.x中的分部。 /确实浮动除法,纠正它在2.x中的整数除法的错误。

这是因为当我们试图划分时,我们隐含地要求浮动除法。 Python将此考虑在内,并根据它完成操作。同样,这是关于猜测意图。当我们添加+时,我们的意图尚不清楚。当我们使用+=时,它非常清楚。

答案 1 :(得分:9)

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

Issue12318

  

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

     

很长一段时间以来,Guido说他不会再这样做了(这是在他的遗憾名单中)。但是,我们不会通过更改代码来破坏代码(list.__iadd__就像list.extend一样工作。)

Issue575536

  

意图是list.__iadd__完全对应   list.extend()。没有必要进行超广泛化   list.__add__()也是:这是一个不这样做的人的特征   想要马丁般的例子可以避免让人惊讶   它们通过使用普通+列表。

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

(感谢@Mouad找到这些)。

答案 2 :(得分:1)

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

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

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

+ =不可交换,因为它包含赋值。在这种情况下,您可以选择左操作数作为结果类型或抛出。令人惊讶的是,a += ba = a + b不同。 a += b不会将英语翻译为“添加到b并将结果分配给”。它转换为“将a添加到b”。这就是为什么它不能用于诸如字符串或元组之类的不可变量。

感谢您的评论。编辑了这篇文章。

答案 3 :(得分:0)

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

记住显式优于隐式。在最常见的情况下,这些猜测都不正确,你不小心试图做一些你没有意图的事情。提出一个TypeError并让你对其进行排序是最安全,最恐怖的事情。