首页 > 解决方案 > 如何从内部装饰器类访问类的实例?

问题描述

我有一个类来处理对服务器的 API 调用。类中的某些方法需要用户登录。由于会话可能会用完,因此我需要一些功能,以便在会话超时后重新登录用户。我的想法是使用装饰器。如果我这样尝试

class Outer_Class():
    class login_required():
        def __init__(self, decorated_func):
            self.decorated_func = decorated_func

        def __call__(self, *args, **kwargs):
            try:
                response = self.decorated_func(*args, **kwargs)
            except:
                print('Session probably timed out. Logging in again ...')
                args[0]._login()
                response = self.decorated_func(*args, **kwargs)
            return response

    def __init__(self):
        self.logged_in = False
        self.url = 'something'
        self._login()

    def _login(self):
        print(f'Logging in on {self.url}!')
        self.logged_in = True

    #this method requires the user to be logged in
    @login_required
    def do_something(self, param_1):
        print('Doing something important with param_1')
        if (): #..this fails
            raise Exception()

我得到一个错误。AttributeError: 'str' object has no attribute '_login' 为什么我没有通过 *args 获得对 Outer_Class 实例的引用?还有另一种方法来获取对实例的引用吗?

找到这个答案How to get instance given a method of the instance? ,但 decorated_function 似乎没有引用它自己的实例。

当我在课堂外使用装饰器功能时,它工作正常。这解决了问题,但我想知道,如果有可能解决这种方式。

标签: python

解决方案


问题在于将对象作为第一个隐藏参数传递的魔力仅适用于非静态方法。由于您的装饰器返回一个不是函数的自定义可调用对象,因此它永远不会接收在调用中丢失的调用对象。因此,当您尝试调用修饰函数时,您只需将其传递param_1self. 你得到第一个异常do_something() missing 1 required positional argument: 'param_1',陷入困境except并得到你的错误。

您仍然可以将装饰器绑定到类,但它必须是一个具有self神奇作用的函数:

class Outer_Class():
    def login_required(decorated_func):
        def inner(self, *args, **kwargs):
            print("decorated called")
            try:
                response = decorated_func(self, *args, **kwargs)
            except:
                print('Session probably timed out. Logging in again ...')
                self._login()
                response = decorated_func(self, *args, **kwargs)
            return response
        return inner
    ...
    #this method requires the user to be logged in
    @login_required
    def do_something(self, param_1):
        print('Doing something important with param_1', param_1)
        if (False): #..this fails
            raise Exception()

然后您可以成功执行以下操作:

>>> a = Outer_Class()
Logging in on something!
>>> a.do_something("foo")
decorated called
Doing something important with param_1

推荐阅读