关于python:使用一个将参数作为派生类函数修饰器的基类函数

Using a base class function that takes parameters as a decorator for derived class function

我觉得在处理常规函数时,我对使用decorator有相当好的理解,但是在派生类中为decorator使用基类的方法和向decorator传递参数之间,我不知道下一步该怎么做。这是一段代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ValidatedObject:
    ...

    def apply_validation(self, field_name, code):
        def wrap(self, f):
            self._validations.append(Validation(field_name, code, f))
            return f
        return wrap


class test(ValidatedObject):
    ....
    @apply_validation("_name","oh no!")
    def name_validation(self, name):
        return name =="jacob"

如果我按原样尝试,我会得到一个"应用程序验证"没有找到。如果我和@self.apply_validation一起尝试,我会发现一个"自我"没有被发现。我也一直在把apply_validation变成一个没有成功的类方法。

有人能解释一下我做错了什么,以及解决这个问题的最佳方法吗?谢谢您。


您遇到的问题是,apply_validation是一个方法,这意味着您需要在ValidatedObject的实例上调用它。不幸的是,在调用它的时候(在test类的定义过程中),没有合适的实例可用。你需要一种不同的方法。

最明显的一个方法是使用一个元类,它通过实例字典(实际上是类字典)进行搜索,并根据找到的内容设置_validations变量。您仍然可以使用decorator,但它可能是一个全局函数,或者可能是一个静态方法,并且它需要以不同的方式工作。下面是一些使用元类和添加函数属性的修饰器的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class ValidatedMeta(type):
    def __new__(meta, name, bases, dct):
        validations = [Validation(f._validation_field_name, f._validation_code, f)
                       for f in dct.values if hasattr(f._validation_field_name)]
        dct["_validations"] = validations
        super(ValidatedMeta, meta).__new__(meta, name, bases, dct)

def apply_validation(field_name, code):
    def decorator(f):
        f._validation_field_name = field_name
        f._validation_code = code
        return f
    return decorator

def ValidatedObject(metaclass=ValidatedMeta):
    pass

class test(ValidatedObject):
    @apply_validation("_name","oh no!")
    def name_validation(self, name):
        return name =="jacob"

运行此代码后,test._validations将成为[Validation("_name","oh no!", test.name_validation)]。请注意,传递给Validation的方法是未绑定的,因此当您调用它时,需要自己传递self参数(或者删除self参数并更改apply_validation中创建的装饰器以返回staticmethod(f))。

如果在继承层次结构的多个级别上定义了验证方法,则此代码可能不会执行所需的操作。上面写的元类只检查立即类的dict中是否有具有适当属性的方法。如果您需要它也包括_validations中继承的方法,您可能需要修改ValidatedMeta.__new__中的逻辑。可能最简单的方法是在bases中查找_validations属性,并将这些列表连接在一起。


在类方法上使用decorators的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from functools import wraps

def VALIDATE(dec):
    @wraps(dec)
    def _apply_validation(self, name):
        self.validate(name)
        return dec(self, name)
    return _apply_validation

class A:
    def validate(self, name):
        if name !="aamir":
            raise Exception, 'Invalid name"%s"' % name

class B(A):

    @VALIDATE
    def name_validation(self, name):
        return name

b = B()
b.name_validation('jacob') # should raise exception