在基础 class 和派生 class 中使用来自基础 class 的装饰器

Using decorator from base class both in base class and derived class

我有一些 python 对象和一些方法,我想在开始时对这些方法进行一些检查,根据此检查,方法的代码将 运行,或者执行将是提高。我不想在每个方法的开头复制 "check" 代码,尽管我做了一个装饰器,我还希望将装饰器嵌入到 class 本身中,因为它与它密切相关。所以基本上:

而不是这个

class A(object):

    def a_method(self):
        if self.check_var is True:
            (some_code)
        else:
            raise Exception

我想要这个

class A(object):

    def decorator(function):
        def function_wrapper(self, *args, **kwargs):
            if self.check_var is True:
                return function(self, *args, **kwargs)
            else:
                raise Exception
        return function_wrapper

    @decorator
    def a_method(self):
        (some_code)

我的第一个问题是,我这样做对吗?或者,还有更好的方法。我有很多 A class 方法需要进行此检查,所以这就是为什么我不想不必要地复制代码。

我的第二个问题是,如果我按照我描述的方式去做,当我想从 class A 中导出 class 并执行相同的装饰检查。同样,我不想复制代码,所以我想重用基础 class A 中的装饰器来执行派生 class 中的检查。我阅读了有关将装饰器变成 @classmethod 的信息,但是当我这样做时,我可以在派生 class 中使用装饰器,但不能再在基础 class 中使用装饰器!

所以基本上我想要这样的东西:

class A(object):

    @classmethod #maybe
    def decorator(function):
        def function_wrapper(self, *args, **kwargs):
            if self.check_var is True:
                return function(self, *args, **kwargs)
            else:
                raise Exception
        return function_wrapper

    @decorator
    def a_method(self):
        (some_code)

class B(A):

    @decorator
    def b_method(self):
        (some_code)

有人知道有什么干净的方法可以做到这一点吗?

My first question is, am I going about this right?

正如 martineau 在下面所说的,好的做法是将 classic 装饰器放在 class 之外。

def get_decorator(function, argument):
    def function_wrapper(*args, **kwargs):
        if argument is True:
            return function(*args, **kwargs)
        else:
            raise Exception
    return function_wrapper

class A(object):
    def __init__(self):
        self.check_var = True
        self.a_method = get_decorator(self.a_method, self.check_var)
    def a_method(self):
        (whatever)

class B(A):
    def __init__(self):
        super(B, self).__init__()
        self.b_method = get_decorator(self.b_method, self.check_var)
    def b_method(self):
        (whatever)

Classic decorator is called during class creation time, which is long before an instance is created. Reference

由于您更愿意将装饰器放在 class 内部(而不是像我在评论中建议的那样在它们外部),下面显示了一种方法。它使装饰器成为 staticmethod 而不是 classmethod,并且需要以稍微不寻常的方式使用它,但仅在 class 的 范围内

有关像这样使用装饰器的必要性的更多信息,请参阅我的问题Calling class staticmethod within the class body?

class A(object):
    @staticmethod
    def decorator(function):
        def function_wrapper(*args, **kwargs):
            print('in function_wrapper')
            return function(*args, **kwargs)
        return function_wrapper

    @decorator.__func__  #### Note unusual decorator usage inside defining class
    def a_method(self):
        print('in a_method')

class B(A):
    @A.decorator  #### Normal decorator usage outside defining class
    def b_method(self):
        print('in b_method')

避免必须使用 __func__ 并仍将定义保留在第一个 class 中的一种方法是推迟将它变成 staticmethod 直到 staticmethod 的最后=31=]定义:

class A(object):
    def decorator(function):
        def function_wrapper(*args, **kwargs):
            print('in function_wrapper')
            return function(*args, **kwargs)
        return function_wrapper

    @decorator
    def a_method(self):
        print('in a_method')

    decorator = staticmethod(decorator)  #### convert for use outside this class

class B(A):
    @A.decorator
    def b_method(self):
        print('in b_method')

另一种避免 __func__ 的方法是这样的:

class A(object):
    class Check:
        @staticmethod
        def decorator(function):
            def function_wrapper(*args, **kwargs):
                print('in function_wrapper')
                return function(*args, **kwargs)
            return function_wrapper

    @Check.decorator
    def a_method(self):
        print('in a_method')

class B(A):
    Check = A.Check

    @Check.decorator
    def b_method(self):
        print('in b_method')

它还有一个额外的好处,就是使装饰器的使用非常统一。