调用实例方法的装饰器
Decorator to invoke instance method
我有一个 class A
方法 do_something(self,a,b,c)
和另一个验证输入并检查名为 can_do_something(self,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
)是因为Dummy
class在validate_input
装饰器得到Dummy
作为参数时还没有定义。我想这可能有不同的实现方式,但现在 Python 是这样工作的。我看到的最简单的解决方案是坚持使用 getattr
,因为它会在 运行 时间查找方法。
我有一个 class A
方法 do_something(self,a,b,c)
和另一个验证输入并检查名为 can_do_something(self,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
)是因为Dummy
class在validate_input
装饰器得到Dummy
作为参数时还没有定义。我想这可能有不同的实现方式,但现在 Python 是这样工作的。我看到的最简单的解决方案是坚持使用 getattr
,因为它会在 运行 时间查找方法。