【问题标题】:Decorator classes in PythonPython 中的装饰器类
【发布时间】:2010-10-14 13:15:09
【问题描述】:

我想按照以下原则构建用作装饰器的类:

  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

【问题讨论】:

  • +1 因为我不确定为什么有人否决了这个问题。这是一个很好的问题:关于 SO 的大多数装饰器问题都不使用类作为装饰器。无论人们如何想到使用类而不是函数,这是一个有效的问题,并且可能完全适合用例。

标签: python decorator


【解决方案1】:

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

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

【讨论】:

  • 我添加了自己的修复程序,因此它可以正常工作。感谢您的帮助,通常有助于开始非常基本的操作。
  • 也许还有一个地方可以将**kwargs 添加到def __call__ (self, *args): 的定义中(以及稍后对self.func 的调用)?
  • 如何将它与 functool.wraps 一起使用?
【解决方案2】:

decorator module 可帮助您编写保留签名的装饰器。

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

【讨论】:

    【解决方案3】:

    要创建一个装饰器来包装函数,使其与原始函数无法区分,请使用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))
    

    【讨论】:

    • 我更喜欢使用类,但我想这是由于我的 Java 背景。我想我读到有人推荐使用类作为装饰器,但我想这是个坏建议。
    • 如果你想使用类,我不确定你把@functools.wraps 放在哪里。我觉得作为装饰器的函数更 Pythonic。而且代码更短。
    • 在您的参数示例中,“func”来自哪里?它不在 mydecorator() 的参数列表中...
    • You apply @decorator(param1, param2) def func: pass - decorator(param1, param2) 返回将用于装饰的实际函数。这里有 3 级嵌套函数。
    猜你喜欢
    • 2019-09-20
    • 2011-04-28
    • 2012-02-09
    • 2013-10-30
    • 1970-01-01
    • 2014-02-16
    • 2015-08-21
    • 1970-01-01
    相关资源
    最近更新 更多