【问题标题】:Run a function until it first returns True, then do not run it again运行一个函数,直到它首先返回 True,然后不再运行它
【发布时间】:2022-01-15 21:47:42
【问题描述】:

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

我的想法是使用装饰器来解决问题,因为我知道它们可以有效地用于运行一个函数,并且永远不会再次运行,这与我想要实现的有点相似。

在以下情况下,数字输入的连续流是: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)

【问题讨论】:

  • @BemwaMalak 在哪里?在wrapper?的else子句中?
  • 可能感兴趣:iter(f, x) 产生对f 的一系列调用,直到f 返回x
  • 解决当前问题的尝试对我来说似乎(至少)很奇怪。为什么不在循环内:if not threshold_reached(args, kw):handler_func(args, kw)
  • @chepner 如果我用 for 循环中的 above_threshold 调用替换该行,使用您建议的内容:iter(above_threshold, True)above_threshold 方法不会打印任何输出。
  • @CristiFati,我不明白你的意思。我想检测 first 值超过阈值的时间。但我不想打破循环。我只想在控制台上指示已达到阈值。只有一次。

标签: python decorator python-decorators


【解决方案1】:

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

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
...

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

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

【讨论】:

    【解决方案2】:

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

    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
    

    【讨论】:

      猜你喜欢
      • 2023-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-04
      • 2014-08-13
      • 2015-12-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多