将while循环转换为可重用的东西

时间:2014-04-14 11:09:49

标签: python

我经常发现自己使用这样的模式:

num_repeats = 123
interval = 12

for _ in xrange(num_repeats):
    result = ...
    if result meets condition:
         break
    time.sleep(interval)

else:
    raise Failed despite multiple attempts

基本上,它会重复代码,直到返回正确的结果或计数器到期为止。

虽然这有效但对我来说看起来太冗长了。有可能"参数化"这个循环到可重用的函数或上下文管理器,例如

with repeat(num_repeats, interval):
    code

或许标准库中有什么东西可以解决这个问题?

3 个答案:

答案 0 :(得分:1)

一种方法是装饰你想要重复的功能:

def repeats_until(num_repeats, interval, condition):
    def deco(f):
        def func(*args, **kwargs):
            for _ in xrange(num_repeats):
                result = f(*args, **kwargs)
                if condition(result):
                    return result
                time.sleep(interval)
        return func
    return deco

然后使用它:

@repeats_until(3, 5, lambda s: s == "hello")
def take_input():
    return raw_input("Say hello: ")

示例(虽然我无法显示等待!)

>>> take_input()
Say hello: foo
Say hello: bar
Say hello: baz
>>> take_input()
Say hello: hello
'hello'

或者,要使用被调用函数保持条件,例如:

def repeats(num_repeats, interval):
    def deco(f):
        def func(*args, **kwargs):
            for _ in xrange(num_repeats):
                result = f(*args, **kwargs)
                if result is not None: # or e.g. False if None is valid return
                    return result
                time.sleep(interval)
        return func
    return deco    

@repeats(3, 5)
def take_input(condition):
    s = raw_input("Say hello: ")
    if condition(s):
        return s

ui = take_input(lambda s: s == "hello")

这依赖于修饰函数返回一个值(在这种情况下是隐式None),它告诉装饰者它还没有完成。

答案 1 :(得分:1)

你肯定无法使用with语句,因为python只在代码运行之前和之后提供钩子,但不提供用于调用它的钩子,即。您无法在with语句中隐藏循环。

一个很好的方法是使用lambda函数:

def repeat(repeats, interval, func):
    for i in xrange(repeats):
        if func(i):
            break
        time.sleep(interval)

然后你可以很容易地使用它:

repeat(123, 12, lambda i: condition(i))

或类似的东西

答案 2 :(得分:1)

您可以使用在返回重复结果之前休眠的生成器。 优点是你的调用者仍然是一个真正的for循环,有 所有breakcontinueelse语义仍然有效。

def trickle_range(num_repeats, interval):
    yield 0
    for k in xrange(1, num_repeats):
          time.sleep(interval)
          yield k


for k in trickle_range(num_repeats, interval):
    ... do stuff, iterate or break as you like ...