运行 一个函数,直到它先 returns 为真,然后再不 运行 它

Run a function until it first returns True, then do not run it again

我正在开发一个接收连续数字输入流的模块。目标是检测 第一次 输入数组超过某个预设阈值的时间。换句话说,我需要运行一个比较函数,直到达到阈值为止;那么该功能需要“关闭”。

我的想法是用装饰器来解决这个问题,因为我知道装饰器可以有效地用于 运行 只用一次并且永远不会再用一个函数,这有点类似于我正在尝试的实现。

在下面的例子中,连续的数字输入流是:12, 19, 82, 92, 26, ...。在这种情况下,预期输出将是:

Rand. val:  12
above_threshold returns False
Rand. val:  19
above_threshold returns False
Rand. val:  82
above_threshold returns True 
Threshold has been reached! 
Comparison function above_threshold shouldn't be called any more.
Rand. val: 92
Rand. val: 26
...

不过目前 above_threshold 在每个循环中都会被调用,我还没有成功地使用装饰器“关闭”该功能。

import time 
import random 

random.seed(12771)

threshold = 75

def run_until_first_true_reached(f):
    """
    Decorator that runs the function f until it first returns True. 
    After returning True once, it will stop running the wrapped function again.
    """
    def wrapper(*args, **kwargs):
        # If f is False
        if not f(*args, **kwargs):
            return f(*args, **kwargs)
        # If f is True
        else: 
            print("Threshold has been reached!")
            print("Comparison function above_threshold shouldn't be called any more.")

            # tried an empty "return" in this line but didn't solve the issue
    return wrapper 

@run_until_first_true_reached
def above_threshold(value, threshold): 
    if value > threshold:
        print("above_threshold returns True")
        return True 
    else:   
        print("above_threshold returns False")
        return False

# Modelling the continuous stream of inputs 
for _ in range(100): 

    rand_val = random.randint(1,100)
    print("Rand. val: ", rand_val)

    above_threshold(rand_val, threshold)

    time.sleep(1)

我不知道有什么方法可以让 decorator/wrapper 在达到条件后不被调用,但是一旦达到条件就将其变成空操作非常简单,通过您的第一条评论似乎是您希望代码执行的操作。

import time 
import random 

random.seed(12771)

threshold = 75

def run_until_first_true_reached(f):
    """
    Decorator that runs the function f until it first returns True. 
    After returning True once, it will stop running the wrapped function again.
    """

    def wrapper(*args, **kwargs):
        if not wrapper.reached:
            v = f(*args, **kwargs)
            # If f is False
            if not v:
                return v
            # If f is True
            else: 
                print("Threshold has been reached!")
                print("Comparison function above_threshold shouldn't be called any more.")
                wrapper.reached = True
        return None   #  ? or wahtever we want to return once the threshold is reached

    wrapper.reached = False
    return wrapper 

@run_until_first_true_reached
def above_threshold(value, threshold): 
    if value > threshold:
        print("above_threshold returns True")
        return True 
    else:   
        print("above_threshold returns False")
        return False

# Modelling the continuous stream of inputs 
for _ in range(100): 

    rand_val = random.randint(1,100)
    print("Rand. val: ", rand_val)

    above_threshold(rand_val, threshold)

    time.sleep(1)

结果:

Rand. val:  12
above_threshold returns False
Rand. val:  19
above_threshold returns False
Rand. val:  82
above_threshold returns True
Threshold has been reached!
Comparison function above_threshold shouldn't be called any more.
Rand. val:  92
Rand. val:  26
Rand. val:  18
Rand. val:  55
...

这里有趣的一点是您需要在某处存储状态...已达到阈值的事实。我在装饰器中这样做的方法是将状态附加到包装器函数。

我稍微改变了你的逻辑,这样包装函数就不会在每次包装器调用时被调用两次。这会产生重复的输出行,导致无法匹配您请求的输出。

简单地使用你想要的想法:“打开和关闭”包装函数的调用。为此,我们可以使用父装饰器函数的变量作为状态标志:

import time 
import random 

random.seed(12771)

threshold = 75

def run_until_first_true_reached(f):
    """
    Decorator that runs the function f until it first returns True. 
    After returning True once, it will stop running the wrapped function again.
    """

    switch_on = True

    def wrapper(*args, **kwargs):
        nonlocal switch_on

        if switch_on:
            threshold_reached = f(*args, **kwargs)
            if threshold_reached:
                print("Threshold has been reached!")
                print("Comparison function above_threshold shouldn't be called any more.")
                switch_on = False

    return wrapper 

@run_until_first_true_reached
def above_threshold(value, threshold): 
    if value > threshold:
        print("above_threshold returns True")
        return True 
    else:   
        print("above_threshold returns False")
        return False

输出:

Rand. val:  12
above_threshold returns False
Rand. val:  19
above_threshold returns False
Rand. val:  82
above_threshold returns True
Threshold has been reached!
Comparison function above_threshold shouldn't be called any more.
Rand. val:  92
Rand. val:  26
Rand. val:  18
Rand. val:  55