39

我想构建用作装饰器的类,并保持以下原则:

  1. 应该可以在 1 个函数的顶部堆叠多个此类装饰器。
  2. 生成的函数名称指针应该与没有装饰器的同一个函数无法区分,可能只是针对它的类型/类。
  3. 除非装饰器实际要求,否则订购装饰器不应该是相关的。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 构造函数中添加“print func.name ”,它将适用于第一个装饰器,但不适用于第二个 - 因为 name 将丢失。

完善eduffy的答案,它似乎工作得很好:

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
4

3 回答 3

24

一个无所事事的装饰器类看起来像这样:

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
于 2009-03-20T13:52:14.703 回答
10

装饰器模块可帮助您编写保留签名的装饰器。

PythonDecoratorLibrary可能会为装饰提供有用的示例。

于 2009-03-20T13:59:10.507 回答
7

要创建一个装饰器来包装函数,使其与原始函数无法区分,请使用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))
于 2009-03-20T14:10:41.027 回答