繁体   English   中英

Python中的函数属性

[英]Function attributes in Python

我问这个问题是Python限制函数执行的延续

我找到了一种无线程等方法。只需简单地检查即可。

这是我的装饰者:

def time_limit(seconds):
    def decorator(func):
        func.info = threading.local()

        def check_timeout():
            if hasattr(func.info, 'end_time'):
                if time.time() > func.info.end_time:
                    raise TimeoutException

        func.check_timeout = check_timeout

        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            if not hasattr(func.info, 'end_time'):
                func.info.end_time = time.time() + seconds
            return func(*args, **kwargs)

        return wrapper

    return decorator

用法:

@time_limit(60)
def algo():
  do_something()
  algo.check_timeout()
  do_something_else()

它在localhost上工作正常,但在使用mod_wsgi和django的服务器apache上失败。

  1. 第一个问题。 请注意hasattr 我应该添加它,因为我'_thread.local'收到错误'_thread.local'没有属性end_time
  2. 为什么我需要threading.local? 正如@Graham Dumpleton指出的那样,我们不能有一个全局结束时间,因为后续请求将进入并覆盖它。 因此,如果第一个请求尚未完成,则其end_time将重置为为后一个请求设置的任何内容。 问题是这种方法没有帮助。 假设我有以下会话的运行。

首次运行 - 在超时发生之前 - 完美运行

第二次运行 - 在超时发生之前 - 完美运行

第三次运行 - 发生超时 - 引发TimeoutException

所有后续调用都会引发TimeoutException无论它是否存在。

似乎所有后续调用都会查看第三次运行的end_time副本,并且由于存在Timeout,因此它们也会引发Timeout

如何为每个函数调用本地化end_time? 谢谢。

编辑:感谢@ miki725和@Antti Haapala我简化了我的功能并使其成为一个简单的类:

class TimeLimit(object):
    def __init__(self, timeout=60):
        self.timeout = timeout
        self.end = None

    def check_timeout(self):
        if self.end and time.time() > self.end:
            raise TimeoutException
        else:
            self.start()

    def start(self):
        if not self.end:
            self.end = time.time() + self.timeout

但是,将定时器传递给函数是非常不方便的,因为algo实际上是非常复杂的递归函数。 所以,我做了以下事情:

timer = TimeLimit() # fails. I think because it is global
def algo()
  do_stuff()
  timer.check_timeout()
  do_another_stuff()
  sub_algo() # check inside it to
  algo()
  ...

有没有办法使timer线程安全。 是伪_timer有什么帮助吗?

问题是您要在函数对象本身上添加end_time 由于每个线程都将导入所有Python模块,因此您只需将end_time设置n次作为运行的线程数(在您的情况下似乎是2)。

要解决这个问题,你可以随时在每个线程中设置end_time ,但这对我来说似乎并不优雅,因为你对将要执行的内容做了几个假设。

其他解决方案是使用类。 这将允许在类实例中保持状态,因此不会发生此问题。

class ExecuteWithTimeout(object):
    def __init__(self, to_execute, timeout):
        self.to_execute = to_execute
        self.timeout = timeout
        self.end = None

    def check_timeout(self):
        if time.time() > self.end:
            raise TimeoutException

    def __call__(self, *args, **kwargs):
        self.end = time.time() + self.timeout
        result = self.to_execute(*args, **kwargs)
        self.check_timeout()
        return result

def usage():
    stuff = ExecuteWithTimeout(do_something, 10)()
    do_something_else(stuff)

另一种方法是使用上下文管理器:

@contextmanager
def timeout_limit(timeout):
    end = time.time() + self.timeout
    yield
    if time.time() > end:
        raise TimeoutException

def usage():
    with timeout_limit(10):
        do_stuff()
    more_things()

或者更好,你可以把两者结合起来!

class TimeLimit(object):
    def __init__(self, timeout=60):
        self.timeout = timeout
        self.end = None

    def __enter__(self):
        self.end = time.time() + self.timeout
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.check_timeout()

    def check_timeout(self):
        if self.end and time.time() > self.end:
            raise TimeoutException

def algo():
    with TimeLimit(2) as timer:
        time.sleep(1)
        timer.check_timeout()
        time.sleep(1)
        timer.check_timeout()

更新您的更新:

timer = TimeLimit() # fails. I think because it is global
def algo():
    ...

使用上面的类对你没有帮助,因为那时类将是一个线程级实例,它会让你回到最初的问题。 问题是保持线程级状态,因此无论是将其存储在类中还是作为函数对象属性都无关紧要。 如果这些函数需要,你的函数应该显式地将状态传递给内部函数。 您不应该依赖使用全局状态来执行此操作:

def algo():
    with TimeLimit(2) as timer:
        do_stuff(timer)
        timer.check_timeout()
        do_more_stuff(timer)
        timer.check_timeout()

问题是hasattr镇守的设置 end_time

  if not hasattr(func.info, 'end_time'): 

    func.info.end_time = time.time() + seconds

每个线程只设置一次end_time 线程是长寿的,并提供许多请求; 现在为每个线程设置一次绝对时间限制,但它永远不会被清除。


至于这个装饰者的用处,我认为它并不聪明; 我会说这是完全丑陋的。 为什么滥用装饰器,与线程本地等战斗以获得单个闭包的功能:

def timelimit_checker(time_limit):
    end = time.time() + time_limit
    def checker():
        if time.time() > end:
            raise TimeoutException

    return checker

def sub_algo(check_limit):
    ...
    check_limit()
    ...

def algo():
    check_limit = timelimit_checker(60)
    ...
    check_limit()
    ...
    subalgo(check_limit)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM