【问题标题】:Short-circuiting list comprehensions [duplicate]短路列表推导[重复]
【发布时间】:2013-05-31 15:19:59
【问题描述】:

在某些情况下,我需要 python 语法来实现短路列表理解或生成器表达式。

这里是一个简单的列表推导,相当于python中的for循环:

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)

该语言不支持短路的理解。建议的语法,以及 python 中的等效 for 循环:

[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

它应该适用于任意迭代,包括无限序列,并且可以扩展到生成器表达式语法。我知道list(itertools.takewhile(lambda x: x != 'potato'), my_list) 是一个选项,但是:

  • 它并不是特别像 Python 那样 - 不如 while 理解那么可读
  • 它可能不如 CPython 理解那么高效或快速
  • 它需要一个额外的步骤来转换输出,而这可以直接放入一个理解中,例如[x.lower() for x in mylist]
  • 甚至是original author doesn't seem to like it much

我的问题是,对于为什么将语法扩展到这个用例不是一个好主意,是否存在任何理论上的问题,或者它只是不可能,因为 python 开发人员认为它很少有用?这似乎是对语言的一个简单补充,也是一个有用的功能,但我可能忽略了一些隐藏的微妙之处或复杂性。

相关:thisthis

【问题讨论】:

  • result = []; any(x=='potato' or result.append(x) for x in my_list)。嗯...不,我认为这比服用时更糟:)
  • 我认为您更有可能在python-dev 邮件列表中获得好的答案。
  • @gnibbler:您刚刚实现了filterfalse,OP 希望在找到第一个“土豆”后停止所有附加。试试:found = []; result = []; any(x=='potato' and not found.append(x) or result.append(x) if not found else None for x in my_list)
  • 另一种语法建议:[x if x != 'potato' else break for x in my_list]
  • list(iter(iter(my_list).next, "potato")) 大声说出来听起来很有趣

标签: python list-comprehension generator-expression


【解决方案1】:

事实证明,正如 @Paul McGuire 所指出的,它已在 PEP 3142 中提出并由 Guido 获得 rejected

我不知道有一个 PEP。我特此拒绝。没有意义 浪费更多时间。

不过,他没有给出解释。在邮件列表中,反对它的一些观点是:

  • “[理解是] 在多个嵌套语句和单个表达式之间精心设计的 1 对 1 转换。但这个提议忽略并打破了这一点。(here)。”也就是说,while 关键字不对应显式循环中的 while - 它只是一个 break
  • “它使 Python 更难学习,因为它增加了要学习的东西。” (引用here
  • 它增加了生成器表达式和列表理解之间的另一个区别。似乎将这种语法添加到列表理解中也是绝对不可能的。

我认为与通常的列表理解的一个基本区别是while 本质上是命令式的,而不是声明式的。它取决于并规定执行顺序,语言(AFAIK)无法保证。我想这就是它没有被包含在 Haskell 的理解中的原因,Python 从中偷走了这个想法。

当然,生成器表达式确实有方向,但它们的元素可能是预先计算的——同样,AFAIK。提到的 PEP 确实提出了 only 用于生成器表达式 - 这是有道理的。

当然,Python无论如何都是命令式语言,但它会引发问题。

从无序集合中选择怎么样?

[x for x in {1,2,3} while x!=2]

在简单的for 循环中也没有它,但这是语言无法强制执行的。 takewhile 回答了这个问题,但这是一个任意的答案。


最后一点,请注意,为了保持一致性,您需要支持dropwhile。像

[x for x in my_list from x != 'potato']

这与等效的for 构造的相关性更小,如果my_list 只是一个可迭代的,这一次它不可能短路。

【讨论】:

  • 你可以像for x in my_set:...一样简单地编写for循环版本。这也没有多大意义做任何有用的事情。
  • 我认为在不引入新关键字(如 [x for x in my_list after x=='potato'] 中的“after”)的情况下缺少 dropwhile 选项一直是采用的障碍之一,但我真的很喜欢这个想法 - 不幸的是,它已作为 PEP 3142 (python.org/dev/peps/pep-3142) 提交并被拒绝。
  • @gnibbler 我明确提到了它,虽然不是很清楚:这是一个任意的答案,最好通过语言避免。在for 循环中根本没有真正的方法来限制它。
  • 和@PaulMcGuire,from 有什么问题?
  • @Elazar - 你需要说[x for x in my_list from x == 'potato']吗?在我看来,这将包括“土豆”条目,而不仅仅是“土豆”之后的所有内容。如果你的意思是“!='potato'”,那么这对我来说更令人困惑,因为'potato'之前的条目也不等于'potato'。为什么 PEP 被拒绝?检查 Python 开发人员列表 (mail.python.org/pipermail/python-dev) 上的存档讨论。
猜你喜欢
  • 1970-01-01
  • 2014-01-28
  • 2018-06-22
  • 2020-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-20
  • 2015-08-04
相关资源
最近更新 更多