【问题标题】:Using iter() with sentinel to replace while loops使用带有哨兵的 iter() 来替换 while 循环
【发布时间】:2017-07-14 14:53:59
【问题描述】:

通常会出现需要无限循环直到达到特定条件的情况。例如,如果我想继续收集随机整数,直到找到一个数字 == n,然后我打破。我会这样做:

import random

rlist = []
n = ...
low, high = ..., ...
while True:
    num = random.randint(low, high)
    if num == n:
        break
    rlist.append(num)

这很有效,但是很笨重。使用iter 有一个更 Pythonic 的替代方案:

iter(o[, sentinel])

返回一个迭代器对象。第一个论点是 根据第二个的存在,解释非常不同 争论。 [...] 如果第二个论点, sentinel, 是给定的,那么 o 必须是一个可调用的对象。迭代器 在这种情况下 created 将调用 o ,每次调用都没有参数 它的next() 方法;如果返回的值等于哨兵, 将引发 StopIteration,否则将返回该值。

上面的循环可以换成

import random
from functools import partial

f = partial(random.randint, low, high)
rlist = list(iter(f, 10))

要将这一原则扩展到已创建的列表,需要稍作改动。我需要像这样定义一个部分函数:

f = partial(next, iter(x)) # where x is some list I want to keep taking items from until I hit a sentinel

其余部分保持不变,但与 while 循环相比,这种方法的主要警告是我不能应用通用布尔条件

例如,我不能应用“在遇到大于 1000 的第一个偶数之前生成数字”。


底线是:除了while循环和iter支持回调哨兵之外,还有另一种选择吗?

【问题讨论】:

  • 为什么while 循环“笨拙”?
  • 谁在不解释的情况下投反对票?有课吗?
  • 它有点笨重和老式。不过我没有什么反对的。只是想探索其他选择。此外,希望得到否决选民的评论。希望有机会纠正任何错误。

标签: python generator


【解决方案1】:

如果您想要通用布尔条件,那么 iter(object, sentinel) 不足以满足您的需求。相比之下,itertools.takewhile() 似乎或多或少是您想要的:它需要一个迭代器,并在给定谓词不再为真时将其切断。

rlist = list(itertools.takewhile(lambda x: x >= 20, inputlist))

顺便说一句,partial 不是 Pythonic,itertools 也不是。 GvR 被记录为不喜欢高阶函数式编程(注意 reduce 在 3.0 中从内置降级为模块成员)。诸如“优雅”和“可读”之类的属性在旁观者的眼中,但是如果您在最纯粹的意义上寻找 Pythonic,那么您需要 while 循环。

【讨论】:

  • 太棒了。这正是我想要的。 iter 能够接受回调作为哨兵。我会接受的。
  • @Sneftel partial 是 GvR 试图摆脱 lambda 的一部分。 itertools 实现了流处理,它与函数式编程有着密切的联系,但不是一回事。两者都不是非 Pythonic,只是不被认为对核心语言至关重要。
  • itertools部分 正在迁移到核心语言。内置的mapfilter 替换了itertools.imapitertools.ifilter,例如。)
猜你喜欢
  • 1970-01-01
  • 2011-01-11
  • 2018-12-10
  • 1970-01-01
  • 1970-01-01
  • 2020-04-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多