Why can't I add a tuple to a list with the '+' operator in Python?
python不支持向列表中添加元组:
1 2 3 4
| >>> [1,2,3] + (4,5,6)
Traceback (most recent call last):
File"<stdin>", line 1, in <module>
TypeError: can only concatenate list (not"tuple") to list |
提供这种语言支持有什么缺点?注意,我希望这是对称的:[1, 2] + (3, 4)和(1, 2) + [3, 4]都将评估为一个全新的清单[1, 2, 3, 4]。我的基本原理是,一旦有人将operator+应用到元组和列表的组合中,他们很可能会再次这样做(很可能在同一表达式中),所以我们也可以提供列表以避免额外的转换。
这是我提出这个问题的动机。
经常会发生这样的情况:为了避免意外的修改和提高性能,我喜欢将小集合存储为元组。然后,我需要将这些元组与列表结合起来,并且必须将它们中的每一个都转换为列表,这就产生了非常难看的代码。
注意,+=或extend可能在简单情况下起作用。但总的来说,当我有一个表情
1
| columns = default_columns + columns_from_user + calculated_columns |
号
我不知道哪一个是元组,哪一个是列表。所以我要么把所有的东西都转换成列表:
1
| columns = list(default_columns) + list(columns_from_user) + list(calculated_columns) |
或使用ITertools:
1
| columns = list(itertools.chain(default_columns, columns_from_user, calculated_columns)) |
。
这两种解决方案都比简单的和更糟糕;而且chain也可能较慢(因为它必须一次迭代一个元素的输入)。
- 简单地保持一致怎么样?使用元组或使用列表,但不能同时使用两者。
- 如果有人支持,我会同意这是最好的行为,但我认为如果有人做了2 + 'abc',他们显然想要'2abc',那么为什么不允许这样做,等等——这解释太多了。显式优于隐式。
- 你可以简单地使用itertools.chain来链接你的东西,而不使用+。
- 我想说,你的论点的弱点是"他们很可能会再这样做"。
- 你不能像那样把EDOCX1[4]连起来。你得去.extend(chain(...))。
- @啊,谢谢。固定的。
- @麦克斯:好的问题是单一的问题。您在这里问了两个不同的"为什么"和"如何"问题。"为什么"是第一个。
- @马特:同意。固定的。
- @麦克斯:我的建议是,不要试图改变内置的行为,而是编写一个简单的函数来连接序列。例如(请原谅为简洁而滥用or)concatentate = lambda *x: reduce(lambda a,b: a.extend(b) or a,x,[])。
这是不支持的,因为+运算符应该是对称的。您想要什么样的退货类型?python zen包含规则
1
| In the face of ambiguity, refuse the temptation to guess. |
。
但是,以下工作是:
1 2
| a = [1, 2, 3]
a += (4, 5, 6) |
。
这里不存在使用什么类型的含糊不清。
- 我希望返回的是一个列表,因为一旦我开始使用+操作符,很可能我需要再次使用它。查看我对问题的更新。
- @麦克斯:我不明白为什么这个总是要返回一个列表。我很高兴python在这种情况下抛出了一个错误,我相信这不会改变。
- @麦克斯:你问过为什么Python不支持向列表中添加元组。imho,Sven的回答突出了这个问题。
- @Svenmarnach你能描述一些情况吗,在这种情况下,你会很高兴tuple+list引发了一个异常,而不是像我描述的那样默默地工作?
- @麦克斯,我可以看到它造成了一些很难追踪的错误。假设您有一个只在元组上操作的方法,但是+运算符执行隐式和静默的强制转换到列表。在代码中,您将var定义为一个元组,然后用一个列表执行一个+=操作。现在您尝试将VaR传递给您的方法和Boom。现在,如果您必须处理在连接集合时显式地确定需要哪种类型,那么跟踪错误就要困难得多。如果您的代码在var后面附加了一个元组或列表,则情况会加倍。
- @SR2222:我同意这是个问题。但是你能给我一个例子,在这种情况下,你想让一个函数在元组上工作,而不是在列表上工作吗?在我看来,这样的功能要么设计得很糟糕,不管+的行为如何,都会给用户带来无尽的烦恼;要么我遗漏了一些明显的原因,为什么它可能有用。
- @Max:有很多函数只适用于元组,而不适用于列表。最值得注意的是,元组可能是可散列的,而列表则不可散列,因此列表不能用作字典键。一般来说,如果我的代码试图添加列表和元组,那么它几乎总是设计错误的标志,所以我想指出这一点。如果这确实是我想要的,我仍然可以添加一个显式转换。
- 哇,我忘了散列…还有一个有趣的观点,关于tuple+list是某种错误的症状;我想这更多的是关于如何在代码中使用这些类型,但是我可以看到这种方法是如何有用的。
- 最后一件事:尽管我现在已经了解了这些好处,但是operator+的行为不是一个严重违反duck类型的例子吗?我认为duck类型是pythonic编程风格的基础之一?我的意思是,当我将一个元组传递给operator +时,它具有列表所具有的所有方法,所以每键入一次duck,python都不应该抱怨……
- @马克斯:鸭子打字并不意味着要用任何类型的力量来执行任何操作。python仍然是一种强类型语言。幸运的是,它不是PHP。
- "+运算符应该是对称的"—好吧,加法运算在某些数系(不是全部)中是对称的。你能链接一份官方文件说明Python中的+操作符应该是对称的吗?否则,这是不正确的。(事实上,我在货币转换代码中不对称地使用它。)
- @Ryanp这更像是一种哲学陈述,而不是严格的技术陈述,因为语言设计的陈述往往是这样。即使对于内置类型,+运算符也不总是可交换的(例如用于字符串连接时),但我仍然认为这个答案准确地描述了导致不允许添加列表和元组的设计决策的基本原理。
为什么python不支持添加不同的类型:简单的答案是它们是不同的类型,如果您试图添加一个iterable并期望一个列表出来呢?我自己也很想再来一个。还考虑到['a','b']+'cd'的输出应该是什么?考虑到显式比隐式更好,所有这样的隐式转换都是不允许的。
为了克服这一限制,请使用extend列表方法添加任何ITerable,例如
1 2
| l = [1,2,3]
l.extend((4,5,6)) |
如果必须添加许多列表/元组,请编写一个函数
1 2 3 4 5 6 7
| def adder(*iterables):
l = []
for i in iterables:
l.extend(i)
return l
print adder([1,2,3], (3,4,5), range(6,10)) |
。
输出:
1
| [1, 2, 3, 3, 4, 5, 6, 7, 8, 9] |
- 他知道怎么做,他想知道为什么他不能用+来做。
- @AGF:"而且必须将它们每一个都转换成列表,这就产生了非常难看的代码。"不,显然他没有,因为以东十一〔一〕不会改变任何东西。
- @Rikpoggi他希望像[1] + (2,) + (3,) + [4] + (5,)这样一个长的表达能够在没有转换或chain或其他类似的东西的情况下立刻完成,而不需要简单。任何在一行中使用extend来实现这一点的代码都将比仅使用加法表达式更糟糕。
- @阿格夫:噢,在运筹学问题上没有类似的东西……
- @rikpoggi"我的理由是,一旦有人将operator+应用到元组和列表的组合中,他们很可能会再次执行此操作(很可能是在同一表达式中),因此我们也可以提供列表以避免额外的转换。"请参阅他对extend的新编辑,他清楚地知道这个问题。
- 对不起,我的错是没有在原来的问题中写更多的细节。请参阅更新。
- @agf,我加了一个adder来增加iterables,这对我来说并不难看。
如果这有助于:
1 2 3 4
| >>> x = [1,2,3]
>>> x += (1,2,3)
>>> x
[1, 2, 3, 1, 2, 3] |
您也可以显式使用列表构造函数,但正如您所提到的,可读性可能会受到影响:
1 2
| >>> list((1,2,3)) + list((1,2,3))
[1, 2, 3, 1, 2, 3] |
号
- 如果我只需要添加一个元组,就可以了。我经常有几个对象,一些元组,一些列表。
- 他的问题是"为什么"。