【问题标题】:Using a base class function that takes parameters as a decorator for derived class function使用带参数的基类函数作为派生类函数的装饰器
【发布时间】:2014-08-05 18:37:21
【问题描述】:

我觉得在处理常规函数时我对使用装饰器有很好的掌握,但是在派生类中使用装饰器的基类方法和将参数传递给所述装饰器之间,我不知道下一步该做什么。 这是一段 sn-p 代码。

类验证对象: ... def apply_validation(self, field_name, code): def 包装(自我,f): self._validations.append(验证(字段名称,代码,f)) 返回 f 返回包装 类测试(ValidatedObject): …… @apply_validation("_name", "哦不!") def name_validation(self, name): 返回名称==“雅各布”

如果我按原样尝试,我会得到“apply_validation”未找到。
如果我用@self.apply_validation 尝试它,我会发现找不到“自我”。
我也一直在搞砸 apply_validation 一个没有成功的类方法。

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

【问题讨论】:

  • 每个方法的每个实例都需要自己的Validation 对象吗?
  • @user2357112 我在想每个测试对象只有一个验证对象。 Validation 对象包含所有验证测试的列表,当您调用一个方法来运行所有验证时,它只是通过列表。
  • @jacobcase94 检查我的答案。这有帮助吗?我不知道ValidatedObject 是什么,所以我尽量做到简单。或者如果我误解了你的问题?
  • 应该ValidatedObject 子类从彼此继承验证?也就是说,如果一个类test2 继承自您的示例代码中的test,那么test2._validations 是否应该包含为"_name" 创建的Validation 实例,还是应该将其留给其他继承的方法?
  • @Blckknght 那会更好。

标签: python decorator


【解决方案1】:

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

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

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 参数并更改在@987654333 中创建的装饰器@返回staticmethod(f))。

如果您在继承层次结构的多个级别定义了验证方法,则此代码可能无法执行您想要的操作。上面写的元类只检查直接类的字典中具有适当属性的方法。如果你还需要在_validations 中包含继承的方法,你可能需要修改ValidatedMeta.__new__ 中的逻辑。可能最简单的方法是在 bases 中查找 _validations 属性并将列表连接在一起。

【讨论】:

  • Python 变得更加复杂。不过这很有帮助,我还有一些处理工作要做。
  • 为什么 new 需要 4 个参数。我很难理解它。我一直在阅读这个问题:stackoverflow.com/questions/100003/… 谢谢。
  • @jacobcase94:元类接受这些参数以匹配其超类type 的调用语法(meta 参数自动提供,如常规__new__ 方法中的cls) .在定义ValidatedObjecttest 类时,调用ValidatedMeta 来构造类对象。你可以手动完成。 ValidatedObject 类语句等价于:ValidatedObject = ValidatedMeta("ValidatedObject", (), {})
【解决方案2】:

只是一个在类方法上使用装饰器的例子:

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

【讨论】:

    猜你喜欢
    • 2019-07-23
    • 1970-01-01
    • 2013-07-02
    • 2017-03-28
    • 2012-08-28
    • 1970-01-01
    • 1970-01-01
    • 2021-02-14
    • 1970-01-01
    相关资源
    最近更新 更多