短路清单理解 [英] Short-circuiting list comprehensions
问题描述
有好几次我都希望使用 python 语法来短路列表推导式或生成器表达式.
这是一个简单的列表推导,相当于python中的for循环:
my_list = [1、2、3,土豆",4、5][如果x!='potato',则x用于my_list中的x]结果= []对于my_list中的元素:如果元素!='potato':result.append(element)
该语言不支持短路的理解.建议的语法,以及等效于python中的循环:
[x代表my_list中的x,而x!='potato']#->[1、2、3]结果= []对于 my_list 中的元素:如果元素!='potato':result.append(element)别的:休息
它应该与任意迭代器(包括无限序列)一起使用,并且可以扩展到生成器表达式语法.我知道 list(itertools.takewhile(lambda x:x!='potato'),my_list)
是一个选项,但是:
- 它不是特别的pythonic-不如一会儿理解
- 它可能无法像CPython理解那样高效或快速
- 需要额外的步骤来转换输出,而可以直接将其转化为理解,例如
[在我的列表中x的x.lower()]
- 甚至原始作者似乎也没有很喜欢.
我的问题是,将语法扩展到此用例不是一个好主意吗?或者由于python开发人员认为它几乎没有用,这是不可能的吗?这似乎是对语言的简单补充和有用的功能,但我可能忽略了一些隐藏的细微之处或复杂之处.
如@Paul McGuire所述,结果是在被Guido拒绝:
我不知道为此有一个PEP.我特此拒绝.没有意义浪费更多的时间.
不过,他没有给出解释.在邮件列表中,有一些反对意见:
- "[理解]是在多个嵌套语句和单个表达式之间精心设计的1对1转换.但是,此建议忽略并破坏了它.( while 关键字与显式循环中的
while
不对应-那里只是一个break
. - 这使Python变得更难学习,因为它增加了另外一件事需要学习."(在此处引用)
- 它在生成器表达式和列表理解之间增加了另一个区别.似乎也将这种语法添加到列表理解中绝对是不可能的.
我认为与通常的列表理解的一个基本区别是 while
本质上是命令性的,而不是声明性的.它取决于并指示执行顺序,而该顺序不受语言(AFAIK)的保证.我想这就是Haskell的理解中不包含它的原因,而python就是从中窃取了这个想法.
当然,生成器表达式确实具有方向,但是它们的元素可以预先计算-再次是AFAIK.提到的PEP确实仅针对生成器表达式提出了建议-这是有道理的.
当然,Python 绝对是一种命令性语言,但这会引发问题.
从无序集合中选择怎么样?
[x用于{1,2,3}中的x,而x!= 2]
您也没有在简单的 for
循环中使用它,但这是您无法通过语言强制执行的操作. takewhile
回答了这个问题,但这是一个任意答案.
最后一点,请注意,为了保持一致性,您需要支持 dropwhile
.像
[x for x in my_list from x != 'potato']
与等价的 for
构造的关系更小,这一次,如果 my_list
仅是可迭代的,则不可能短路.
On several occasions I've wanted python syntax for short-circuiting list comprehensions or generator expressions.
Here is a simple list comprehension, and the equivalent for loop in python:
my_list = [1, 2, 3, 'potato', 4, 5]
[x for x in my_list if x != 'potato']
result = []
for element in my_list:
if element != 'potato':
result.append(element)
There isn't support in the language for a comprehension which short-circuits. Proposed syntax, and equivalent for loop in python:
[x for x in my_list while x != 'potato']
# --> [1, 2, 3]
result = []
for element in my_list:
if element != 'potato':
result.append(element)
else:
break
It should work with arbitrary iterables, including infinite sequences, and be extendible to generator expression syntax. I am aware of list(itertools.takewhile(lambda x: x != 'potato'), my_list)
as an option, but:
- it's not particularly pythonic - not as readable as a while comprehension
- it probably can't be as efficient or fast as a CPython comprehension
- it requires an additional step to transform the output, whereas that can be put into a comprehension directly, e.g.
[x.lower() for x in mylist]
- even the original author doesn't seem to like it much.
My question is, was there any theoretical wrinkle about why it's not a good idea to extend the grammar to this use case, or is it just not possible because python dev think it would be rarely useful? It seems like a simple addition to the language, and a useful feature, but I'm probably overlooking some hidden subtleties or complications.
Turns out, as @Paul McGuire noted, that it was proposed in PEP 3142 and rejected by Guido:
I didn't know there was a PEP for that. I hereby reject it. No point wasting more time on it.
He doesn't give explanations, though. In the mailing list, some of the points against it are:
- "[comprehension are] a carefully designed 1 to 1 transformation between multiple nested statements and a single expression. But this proposal ignores and breaks that. (here)." That is, the
while
keyword does not correspond to awhile
in the explicit loop - it is only abreak
there. - "It makes Python harder to learn because it adds one more thing to learn." (citated here)
- It adds another difference between generator expressions and list-comprehension. Seems like adding this syntax to list comprehension too is absolutely out of the question.
I think that one basic difference from the usual list comprehension is that while
is inherently imperative, not declarative. It depends and dictates an order of execution, which is not guaranteed by the language (AFAIK). I guess this is the reason it is not included in Haskell's comprehensions, from which python stole the idea.
Of course, generator expressions does have direction, but their elements may be precomputed - again, AFAIK. The PEP mentioned did propose it only for generator expressions - which makes some sense.
Of course, Python is an imperative language anyway, but it will raise problems.
What about choosing out of a non-ordered collection?
[x for x in {1,2,3} while x!=2]
You don't have it in simple for
loops too, but that's something you can't enforce by the language. takewhile
answers this question, but it is an arbitrary answer.
One last point, note that for consistency you will want support for dropwhile
. something like
[x for x in my_list from x != 'potato']
Which is even less related to the equivalent for
construct, and this time it is not possibly short circuit if my_list
is just an iterable.
这篇关于短路清单理解的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!