python - 装饰器调用实例方法

标签 python django decorator

我有一个类 A ,其方法为 do_something(self,a,b,c) 和另一个验证输入并检查名为 can_do_something 的权限的实例方法(自身,a,b,c)

这是我的代码中的常见模式,我想编写一个接受验证函数名称并执行测试的装饰器。

def validate_input(validation_fn_name):
        def validation_decorator(func):
            def validate_input_action(self,*args):
                error = getattr(self,validation_fn_name)(*args)
                if not error == True:
                    raise error
                else:
                    return func(*args)
            return validate_input_action
        return validation_decorator

调用函数如下

@validate_input('can_do_something')
def do_something(self,a,b,c):
   return a + b + c

问题是我不确定如何在验证函数中维护self。我已将验证 fn 名称与 getattr 一起使用,因此 fn 可以在实例的上下文中运行,但我无法对 func(*args) 执行此操作。

那么实现这一目标的正确方法是什么?

谢谢。

编辑

因此,在 @André Laszlo 回答之后,我意识到 self 只是第一个参数,因此根本不需要使用 getattr,只需传递 *args 即可。

def validate_input(validation_fn):
    def validation_decorator(func):
        def validate_input_action(*args):
            error = validation_fn(*args)
            if not error == True:
                raise error
            else:
                return func(*args)
        return validate_input_action
    return validation_decorator

更加优雅,它还支持静态方法。

向 @André Laszlo 示例添加静态方法证明装饰器正在工作:

 class Foo(object):
    @staticmethod
    def validate_baz(a,b,c):
       if a > b:
          return ValueError('a gt b')

    @staticmethod
    @validate_input(Foo.validate_baz)
    def baz(a,b,c):
       print a,b,c

    >>> Foo.baz(1,2,3)
    1 2 3
    >>> Foo.baz(2,1,3)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 6, in validate_input_action
    ValueError: a gt b

但是,当我尝试在 django 模型中做同样的事情时:

from django.db import models
from django.conf import settings

settings.configure()

class Dummy(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=10)

    def can_say_name(self):
        if name is None:
            return Exception('Does not have a name')

    @validate_input(can_say_name)
    def say_name(self):
        print self.name

    @staticmethod
    def can_create_dummy(name):
        if name == 'noname':
            return Exception('No name is not a name !')

    @staticmethod
    @validate_input(Dummy.can_create_dummy)
    def create_dummy(name):
        return Dummy.objects.create(name=name)

我得到以下信息:

NameError: name 'Dummy' is not defined

那么在这个问题上,django 模型和对象有什么不同呢?

最佳答案

我认为这符合你的要求:

def validate_input(validation_fn_name):
    def validation_decorator(func):
        def validate_input_action(self, *args):
            error = getattr(self, validation_fn_name)(*args)
            if error is not None:
                raise error
            else:
                arglist = [self] + list(args)
                return func(*arglist)
        return validate_input_action
    return validation_decorator

class Foo(object):

    def validate_length(self, arg1):
        if len(arg1) < 3:
            return ValueError('%r is too short' % arg1)

    @validate_input('validate_length')
    def bar(self, arg1):
        print "Arg1 is %r" % arg1


if __name__ == "__main__":
    f = Foo()
    f.bar('hello')
    f.bar('')

输出是:

Arg1 is 'hello'
Traceback (most recent call last):
  File "validator.py", line 27, in <module>
    f.bar('')
  File "validator.py", line 6, in validate_input_action
    raise error
ValueError: '' is too short

更新答案

发生错误(NameError: name 'Dummy' is not Defined)是因为在 validate_input 装饰器时尚未定义 Dummy 类获取 Dummy 作为参数。我想这可以用不同的方式实现,但现在这就是 Python 的工作方式。我看到的最简单的解决方案是坚持使用 getattr,它会起作用,因为它在运行时查找方法。

关于python - 装饰器调用实例方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28085694/

相关文章:

django - 在 SimpleHTTPServer 中运行 Django 框架?

python - 如何在基于类的 View (模板 View )中进行多个模型查询

python - 用@staticmethod 修饰 __call__

python - Graphviz 错误 - 如何使用 Python 3.3 查看图形?

python - Plotly:如何绘制多个 y 轴?

python - django allauth登录测试无法正确登录

java - 装饰器模式 - 多个包装器或数量属性?

python - 将方法传递给带有参数的装饰器?

python - 为可执行文件指定一个不同于可执行脚本名称的名称

php - Python/PHP Tesseract 输出优化技巧