【问题标题】:What is the most pythonic way to have a generator expression executed?执行生成器表达式的最 Pythonic 方式是什么?
【发布时间】:2011-03-13 16:38:52
【问题描述】:

越来越多的 Python 特性变成了“惰性可执行文件”,比如生成器 表达式和其他类型的迭代器。 然而,有时我看到自己想要滚动一个单行“for”循环,只是为了执行一些操作。

真正执行循环的最 Pythonic 的事情是什么?

例如:

a = open("numbers.txt", "w")
(a.write ("%d " % i) for i in xrange(100))
a.close()

不是实际的代码,但你明白我的意思。相反,如果我使用列表生成器,我会产生一个副作用,即创建一个填充了“无”的 N 长列表。

目前我所做的是将表达式用作调用“any”或“all”的参数。但我想找到一种不依赖于循环中执行的表达式的结果的方法——“any”和“all”都可以根据评估的表达式停止。

需要明确的是,这些是我已经知道的方法,并且每种方法都有其缺点:

[a.write ("%d " % i) for i in xrange(100))]

any((a.write ("%d " % i) for i in xrange(100)))

for item in (a.write ("%d " % i) for i in xrange(100)): pass

【问题讨论】:

  • "N 长列表,里面有修女。"
  • 澄清一下:我很少在生产代码中使用这样的东西——考虑到这里的反应,我可能会完全停止。但是我喜欢写单行的东西(尽管这些东西本质上是不合常规的),这是我最想念的地方。就在昨天,我花了很长时间试图为 ruby​​ 的 1.9.2 “chunck”列表方法获得一个单行。 (单行版本很糟糕——但是在“执行的生成器”之前声明字典的多行给 Python 提供了比“chunck”更好的东西)
  • @Nas:停止随意指责人们没有做过的事情。
  • @jsbueno,对于你想要 Haskell 论坛的单行者:“sequence_ $ map print [0..99]”。嗯嗯。 :-)
  • 没有不好的问题,只有不好的做法。致所有仇恨者:请停止对任何敢于回答的人投反对票!知识就是力量 - 如果你想要 forbiden fruit 概念,你应该做宗教而不是编程;-)

标签: python lazy-evaluation generator


【解决方案1】:

有一种明显的方法可以做到这一点,这就是你应该这样做的方式。没有任何借口可以巧妙地做到这一点。

a = open("numbers.txt", "w")
for i in xrange(100):
    a.write("%d " % i)
d.close()

延迟执行给您带来了很大的好处:它允许您将一个序列传递给另一段代码,而无需将整个事物保存在内存中。它用于创建高效的序列作为数据类型。

在这种情况下,您不希望延迟执行。你想要处决。你可以……执行。带有for 循环。

【讨论】:

  • 在这种情况下(写入文件),这可能是一个很好的建议,但我经常发现用例,for 循环会过于冗长。例如:for a, b, c, d, e, f in an_iterable: my_function(a, b, c, d, e, f) 很长,它暗示的不仅仅是将函数应用于可迭代对象。使用类似:exec_map(my_function, an_iterable) 的内容要短得多,至少在我看来,更优雅。
【解决方案2】:

如果我想做这个具体的例子,我会写

for i in xrange(100): a.write('%d ' % i)

如果我经常需要使用迭代器来实现它的效果,我会定义

def for_effect(iterable):
    for _ in iterable:
        pass

【讨论】:

  • 我认为 for 循环看起来比 2 行更好。我们喜欢 Python 的缩进空格
  • 是的,我通常也会这样写,但 OP 可能不知道单行形式,并专门要求提供。
【解决方案3】:

有许多accumulators 具有消耗它们给出的整个可迭代的效果,例如minmax——但即使它们也不会完全忽略过程中产生的结果(@例如,如果某些结果是复数,987654325@ 和 max 将引发异常)。我不认为有一个内置的累加器可以完全满足您的需求——您必须编写(并添加到您个人的微型实用程序函数中)一个微型实用程序函数,例如

def consume(iterable):
    for item in iterable: pass

我猜主要原因是 Python 有一个 for 语句,当它像手套一样适合时你应该使用它(即,对于你想要 consume 的情况;-) .

顺便说一句,a.write 返回None,这是错误的,所以any 实际上会消耗它(a.writelines 会做得更好!)。但我意识到你只是举个例子;-)。

【讨论】:

  • 实际上,有种方法可以在没有min/max 的缺点的情况下完成所要求的任务 - 在一行中并且无需创建 len(N) 列表。经过一夜好眠后,我发现了 3(三)种不同的方式 - 但用 Fermat 的话来说 - '这个空白太窄,无法包含 [这个真正奇妙的证据]' :-)
  • @Nas,有明显的愚蠢方式,例如[x for x in iterable if False](创建一个空列表)和类似的any(x and False for x in iterable)(根本不创建任何列表)和all(x or True for x in iterable)——但是(虽然它们确实适合这个边缘;-) 它们都非常值得畏缩。
  • @Nas,好吧..随意使用主“页面”而不是边距。 :-)
  • @Alex:啊,很好——你提到了我想到的一种方式。关于您展示的另外两个 - 我想知道,我们是否保证 bool(x) 适用于每种类型? (我认为这大约相当于能够将对象与运算符and/or/not 一起使用)? PS。不,我没有说我找到了任何 prettypythonic 方式
  • @jsbueno:嗯,我不知道 - 又被投票否决了?有人扔赏金骨头吗? :)
【解决方案4】:

现在是 2019 年 - 这是一个从 2010 年开始出现的问题。 recent thread in one of Python's mailing lists spammed over 70 e-mails on this subject,但他们再次拒绝向该语言添加 consume 调用。

在那个线程上,实际上出现了最有效的模式,而且还远非显而易见,所以我将它作为答案发布在这里:

import deque

consume = deque(maxlen=0).extend 

然后使用 consume 可调用来处理生成器表达式。

事实证明,cPython 中的 deque 本机代码实际上针对 maxlen=0 情况进行了优化,并且只会消耗可迭代对象。
我在问题中提到的 anyall 调用应该同样有效,但是必须担心表达式的真实性才能使用迭代。


我认为这仍然可能存在争议,毕竟,明确的两行 for 循环可以处理这个问题 - 我记得这个问题是因为我刚刚提交了一个提交,我创建了一些线程,然后开始,然后加入然后返回 - 没有consume callable,即 4 行,大部分是样板代码,并且没有受益于循环遍历本机代码中的可迭代对象: https://github.com/jsbueno/extracontext/blob/a5d24be882f9aa18eb19effe3c2cf20c42135ed8/tests/test_thread.py#L27

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-03
    • 2016-02-29
    • 1970-01-01
    • 1970-01-01
    • 2011-09-23
    • 2018-07-26
    • 2022-01-01
    相关资源
    最近更新 更多