Python装饰器用于访问类变量

4

我有一个装饰器来控制时间限制,如果函数执行超过限制,会抛出一个错误。

def timeout(seconds=10):
    def decorator(func):
        # a timeout decorator
    return decorator

我希望构建一个类,使用构造函数将时间限制传递到类中。

def myClass:
    def __init__(self,time_limit):
        self.time_limit = time_limit

    @timeout(self.time_limit)
    def do_something(self):
        #do something

但这并不起作用。

File "XX.py", line YY, in myClass
    @timeout(self.tlimit)
NameError: name 'self' is not defined

这个问题的正确实现方式是什么?

1
欢迎来到SO。短语“这不起作用”是无用的。它对任何人都没有帮助,也没有描述任何问题。什么不起作用,发生了什么,你期望发生什么? - dfundako
1
假设您的装饰器有效,您可以执行类似于 self.do_something = timeout(time_limit)(self.do_something) 的操作。这将向每个类实例添加一个函数对象。但是,self 不在类主体的作用域内。 - juanpa.arrivillaga
@dfundako,我更新了,'does not work' 意味着问题无法运行,并附上了我发布的错误信息。 - MTANG
3
@MTANG 这不是定义一个类的正确方式... - dfundako
2
你是指实例初始化吗?是的。那段代码位于类体内,在类定义时间执行,即在类初始化期间,在创建任何实例之前就执行了。self只是一个函数的第一个参数。它像其他任何参数一样工作,即在调用函数时创建一个局部变量。 - juanpa.arrivillaga
显示剩余7条评论
2个回答

3

self.time_limit只有在调用类实例的方法时才可用。

另一方面,装饰器语句是在解析类主体时运行的。

然而,如果您的装饰器内部始终应用于方法,则其内部部分将获得self作为其第一个参数 - 在那里您可以简单地利用任何实例属性:

def timeout(**decorator_parms):
    def decorator(func):
        def wrapper(self, *args, **kwargs):
             time_limit = self.time_limit
             now = time.time()
             result = func(self, *args, **kwargs)
             # code to check timeout
             ..
             return result
        return wrapper
    return decorator

如果你的装饰器需要使用其他时间限制而不是总是使用 self.limit ,你可以始终传递一个字符串或其他常量对象,并在内部装饰器中使用简单的 if 语句进行检查。如果超时时间是某个特定的字符串或对象,则使用实例属性,否则使用传递的值;


它能工作!谢谢。我也想知道为什么我需要使用@timeout()而不是@timeout才能使其工作? - MTANG
你需要额外的 (),因为它最初被设计为一个“带参数的装饰器” - 这需要一个外层函数(上面调用的函数名为 timeout)来记录这些参数。如果你不使用任何参数,则根本不需要外层函数,当前的中间函数(def decorator(func):)可以放在外层。如果你这样做了,那么多余的括号就不再需要了。 - jsbueno
非常感谢!我对装饰器不是很熟悉。 - MTANG

0

你也可以在构造函数中装饰一个方法:

def myClass:
    def __init__(self,time_limit):
        self.do_something = timeout(time_limit)(self.do_something)
    
    def do_something(self):
        #do something

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接