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