Skip to main content
 首页 » 编程设计

python之使用带有哨兵的 iter() 来替换 while 循环

2025年05月04日21bonelee

经常会出现需要无限循环直到达到特定条件的情况。例如,如果我想继续收集随机整数,直到找到一个数字 == 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])

Return an iterator object. The first argument is interpreted very differently depending on the presence of the second argument. [...] If the second argument, sentinel, is given, then o must be a callable object. The iterator created in this case will call o with no arguments for each call to its next() method; if the value returned is equal to sentinel, StopIteration will be raised, otherwise the value will be returned.

上面的循环可以换成

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 循环相比,此方法的主要警告是我不能应用通用 bool 条件。

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


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

请您参考如下方法:

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

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

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