使用@语法的Python装饰器参数

7 投票
1 回答
1343 浏览
提问于 2025-04-17 21:25

我正在尝试使用一个可以接受参数的缓存属性装饰器。

我查看了这个实现:http://www.daniweb.com/software-development/python/code/217241/a-cached-property-decorator

from functools import update_wrapper 

def cachedProperty (func ,name =None ):
  if name is None :
    name =func .__name__ 
  def _get (self ):
    try :
      return self .__dict__ [name ]
    except KeyError :
      value =func (self )
      self .__dict__ [name ]=value 
      return value 
  update_wrapper (_get ,func )
  def _del (self ):
    self .__dict__ .pop (name ,None )
  return property (_get ,None ,_del )

但我遇到的问题是,如果我想使用参数,就不能用@这种语法来调用装饰器:

@cachedProperty(name='test') # This does NOT work
def my_func(self):
    return 'ok'

# Only this way works
cachedProperty(my_func, name='test')

如何在装饰器中使用@语法并传递参数呢?

谢谢

1 个回答

9

你需要一个装饰器工厂,也就是一个可以生成装饰器的包装器:

from functools import wraps 

def cachedProperty(name=None):
    def decorator(func):
        if decorator.name is None:
            decorator.name = func.__name__ 
        @wraps(func)
        def _get(self):
            try:
                return self.__dict__[decorator.name]
            except KeyError:
                value = func(self)
            self.__dict__[decorator.name] = value 
            return value 
        def _del(self):
            self.__dict__.pop(decorator.name, None)
        return property(_get, None, _del)
    decorator.name = name
    return decorator

用法如下:

@cachedProperty(name='test')
def my_func(self):
    return 'ok'

装饰器其实就是一种语法糖,简单来说就是:

def my_func(self):
    return 'ok'
my_func = cachedProperty(name='test')(my_func)

只要@后面的表达式能返回你的装饰器[*],那么这个表达式具体做什么都无所谓。

在上面的例子中,@cachedProperty(name='test')这一部分首先会执行cachedProperty(name='test'),然后这个调用的返回值就被用作装饰器。在这个例子中,返回的是decorator,所以my_func函数就通过调用decorator(my_func)来被装饰,而这个调用的返回值是property对象,因此这就是替代my_func的内容。


[*] @的表达式语法是故意限制了它能做的事情。你只能进行属性查找和调用,仅此而已,decorator的语法规则只允许在点号后面可选地加上带参数的调用:

decorator               ::=  "@" dotted_name ["(" [argument_list [","]] ")"] NEWLINE)

这是对语法的一种故意限制

撰写回答