0

一些装饰器应该只用在最外层。

增强原始功能并添加配置参数的装饰器就是一个示例。

from functools import wraps

def special_case(f):
    @wraps(f)
    def _(a, b, config_x=False):
        if config_x:
           print "Special case here"
           return
        return f(a, b)

我怎样才能避免像这样的装饰器被另一个装饰器装饰?

编辑

让每个尝试应用新装饰器的人都担心应用顺序,真是令人作呕。

那么,有没有可能避免这种情况呢?是否可以在不引入新参数的情况下添加配置选项?

4

2 回答 2

5

没有任何方法可以阻止它被装饰。您只需要记录它需要最后应用并告诉人们不要在另一个装饰器中使用它。

编辑响应您的编辑:在 Python 3 中,您可以为函数提供仅关键字参数。这大大减少了更改对该功能的现有用途的影响。不幸的是,这只适用于 Python 3。

最终,将装饰器应用于函数只是意味着将装饰函数作为参数传递给另一个函数。函数(或任何对象)甚至无法知道是作为参数传递的,更不用说传递给它的对象了。你不知道后面的装饰器的原因与在普通函数调用中一样的原因f(g(x)),该函数g无法知道它稍后会被调用f

这是编写装饰器很棘手的原因之一。依赖于大量使用装饰器的代码,这些装饰器将显式参数传递给它们的包装函数(当你的传递aandb时)本质上是脆弱的。幸运的是,很多时候您可以编写一个使用的装饰器,*args因此**kwargs它可以将所有不使用的参数传递给装饰函数。

如果有人使用您提供的代码,并编写了另一个仅显式接受ab作为参数的装饰器,然后将装饰函数调用为f(a, b, True),如果失败,那是他们自己的错。他们应该知道他们使用的其他装饰器可能已经更改了函数签名。

于 2012-09-04T03:00:45.033 回答
1

通常,当一个人编写一个一般使用的装饰器时,不会限制它所包装的函数的参数数量或名称。

大多数装饰器接受位置参数列表,并将关键字参数映射为包装器的参数,并将收到的这些参数传递给装饰函数:

def deco(func):
   def wrapper(*args, **kwargs):
       ... decorator stuff here ...
       return func(*args, **kwargs)

因此,如果装饰器要接收它应该“使用”的参数 - 就像config_x你提到的那样,你所要做的就是记录它,将它作为关键字参数,然后从kwargs. 为了避免参数名称冲突,例如,可以在此参数名称前加上装饰器自己的名称或其他不同的名称:

def deco(func):
   def wrapper(*args, **kwargs):
       if "deco_config_x" in kwargs):
           config_x = kwargs.pop(deco_config_x)
       ... decorator stuff here ...
       return func(*args, **kwargs)

这样,装饰器可以放在“装饰器堆栈”上的任何位置——它会选择寻址到它的参数,而那些在它下面的参数不会得到任何奇怪的参数。唯一的要求是你的函数和装饰器作为一个整体突出让他们不知道的关键字参数通过。

于 2012-09-04T03:31:03.707 回答