装饰链

时间:2015-07-29 11:41:47

标签: python decorator python-decorators

我正在建立一些基于Django' @permission_required装饰器的装饰器。这些装饰器只需使用不同的参数调用permission_required装饰器。

例如:

def permission_required_ajax(perm):
    return permission_required(perm, raise_exception=True)

这很好用,我有几个这样的例子。

问题在于我的装饰师完全不接受任何参数:

def special_permission():
    return permission_required_ajax('special_permission')

这不起作用。当我使用@special_permission装饰器修饰函数时,出现以下错误:

  

TypeError:special_permission()接受0个位置参数,但是给出了1个

我错过了什么?

2 个答案:

答案 0 :(得分:5)

你还需要调用你的装饰工厂

@special_permission()
def function():
    # ...

@符号产生后的表达式用作装饰器;调用装饰器,传递函数进行装饰。

换句话说,装饰器是语法糖:

def function():
    # ...
special_permission()(function)

如果您不进行()调用,则会将该函数传递给您的包装器。

或者,让装饰者接受该函数,并将其直接传递给permission_required_ajax()调用产生的装饰器:

def special_permission(func):
    return permission_required_ajax('special_permission')(func)

然后在不调用的情况下使用它(它现在是装饰器,而不是装饰器工厂):

@special_permission  # no () call now
def function():
    # ...

答案 1 :(得分:1)

装饰器参数如何工作

当装饰器没有参数时,它会传递正在装饰的函数/类。当它有参数时,它首先用参数调用,然后在被装饰的函数/类中调用返回的内容。

回顾一下:

# Case 1, no arguments

@foo
def bar(): pass

将调用函数foo传递函数bar,然后将返回的任何内容绑定到名称bar

# Case 2

@foo(1, 2, 3)
def bar(): pass

函数foo被称为传递1,2和3作为参数。然后调用foo返回的内容,将bar函数作为参数传递,返回的内容是绑定到名称bar

对于您的情况

解决方案可能只是在special_permission装饰器调用中添加括号...

@special_permission()  # <-- note the parenthesis
def bar(): ...

或者,如果您不想在使用装饰器时添加括号,可以将其实现为:

def special_permission(f):
    return permission_required_ajax('special_permission')(f)