题
我想构造用作装饰器的类,并完好无损地遵循以下原则:
- 应该可以在 1 个函数之上堆叠多个此类类装饰器。
- 生成的函数名指针应该与没有装饰器的同一函数无法区分,也许只是为了它是什么类型/类。
- 除非装饰者实际强制要求,否则对装饰者的排序不应该是相关的。IE。独立的装饰器可以按任何顺序应用。
这是针对 Django 项目的,我现在正在处理的特定情况该方法需要 2 个装饰器,并显示为普通的 python 函数:
@AccessCheck
@AutoTemplate
def view(request, item_id) {}
@AutoTemplate 更改了函数,以便它不返回 HttpResponse,而是仅返回在上下文中使用的字典。使用 RequestContext,并从方法名称和模块推断模板名称。
@AccessCheck 根据 item_id 对用户添加额外的检查。
我猜这只是为了让构造函数正确并复制适当的属性,但是这些属性是什么?
以下装饰器将无法按照我的描述工作:
class NullDecl (object):
def __init__ (self, func):
self.func = func
def __call__ (self, * args):
return self.func (*args)
如以下代码所示:
@NullDecl
@NullDecl
def decorated():
pass
def pure():
pass
# results in set(['func_closure', 'func_dict', '__get__', 'func_name',
# 'func_defaults', '__name__', 'func_code', 'func_doc', 'func_globals'])
print set(dir(pure)) - set(dir(decorated));
此外,尝试添加“打印功能”。姓名" 在 NullDecl 构造函数中,它将适用于第一个装饰器,但不适用于第二个 - 因为名称将丢失。
精制 埃达菲的回答了一下,似乎效果很好:
class NullDecl (object):
def __init__ (self, func):
self.func = func
for n in set(dir(func)) - set(dir(self)):
setattr(self, n, getattr(func, n))
def __call__ (self, * args):
return self.func (*args)
def __repr__(self):
return self.func
解决方案
一个什么都不做的装饰器类看起来像这样:
class NullDecl (object):
def __init__ (self, func):
self.func = func
for name in set(dir(func)) - set(dir(self)):
setattr(self, name, getattr(func, name))
def __call__ (self, *args):
return self.func (*args)
然后就可以正常应用了:
@NullDecl
def myFunc (x,y,z):
return (x+y)/z
其他提示
这 装饰器模块 帮助您编写保留签名的装饰器。
还有 Python装饰器库 可能为装饰者提供有用的例子。
要创建一个将函数包装在一个与原始函数无法区分的事物中的装饰器,请使用 functools.wraps
.
例子:
def mydecorator(func):
@functools.wraps(func):
def _mydecorator(*args, **kwargs):
do_something()
try:
return func(*args, **kwargs)
finally:
clean_up()
return _mydecorator
# ... and with parameters
def mydecorator(param1, param2):
def _mydecorator(func):
@functools.wraps(func)
def __mydecorator(*args, **kwargs):
do_something(param1, param2)
try:
return func(*args, **kwargs)
finally:
clean_up()
return __mydecorator
return _mydecorator
(我个人偏好是使用函数而不是类创建装饰器)
装饰器的顺序如下:
@d1
@d2
def func():
pass
# is equivalent to
def func():
pass
func = d1(d2(func))
不隶属于 StackOverflow