从另一个装饰器(python)创建装饰器

时间:2017-06-14 14:47:11

标签: python python-3.x python-decorators

在python中关于装饰器的主题花了几个小时之后,我仍然有两个问题。

首先;如果你有没有参数的装饰器,sytntax是这样的:

@decorator
def bye():
    return "bye"

这只是一个语法糖,与此相同

bye = decorator(bye)

但如果我有一个带有参数的装饰器:

@decorator(*args)
def bye():
    return "bye"

“无糖”版本如何?函数是作为参数之一传入的吗?

bye = decorator("argument", bye)

第二个问题(与第一个更实际的例子有关);

def permission_required(permission):
    def wrap(function):
        @functools.wraps(function)
            def wrapped_func(*args, **kwargs):
                if not current_user.can(permission):
                    abort(403)
                return function(*args, **kwargs)
            return wrapped_function
    return wrap

def admin_required(f):
    return permission_required(Permission.ADMINISTER)(f)

此处 permission_required 装饰器将传递给新创建的名为 admin_required 的装饰器的返回语句。我不知道这是如何工作的。主要是返回语句,我们返回原始装饰器+函数(用奇怪的语法)。有人可以详细说明吗? - 非常欢迎细节

2 个答案:

答案 0 :(得分:3)

简单地调用带有参数的装饰器(使用该参数),以生成另一个装饰器。然后像往常一样用装饰函数作为参数调用该装饰器。所以翻译:

super()

将是:

@decorator(*args)
def bye():
    return "bye"

或者你可能会发现更清楚:

bye = decorator(*args)(bye)

(当然,实际上并没有创建temp = decorator(*args) bye = temp(bye) 变量。)

在第二期中,temp被定义为@admin_required的快捷方式。

答案 1 :(得分:2)

在装饰符号中给出参数时,

@decorator(a, b, c)
def function(): pass

这是写作的语法糖

def function(): pass

function = decorator(a, b, c)(function)

即,使用参数a,b,c调用decorator,然后使用唯一参数function调用它返回的对象

当装饰器是一个类时,最容易理解它是如何有意义的。我将使用您的permission_required装饰器作为运行示例。它原本可以写成:

class permission_required:
    def __init__(self, permission):
        self.permission = permission

    def __call__(self, function):
        @functools.wraps(function)
        def wrapped_func(*args, **kwargs):
            if not current_user.can(permission):
                abort(403)
            return function(*args, **kwargs)
        return wrapped_func

admin_required = permission_required(Permission.ADMINISTER)

使用装饰器时,例如

@permission_required(Permission.DESTRUCTIVE)
def erase_the_database():
    raise NotImplemented # TBD: should we even have this?

首先实例化该类,将Permission.DESTRUCTIVE传递给__init__,然后将该实例作为函数调用erase_the_database作为参数,调用__call__方法,构造包装函数并返回它。

以这种方式思考,admin_required应该更容易理解:它是permission_required类的一个实例,尚未被调用。它主要用于速记:

@admin_required
def add_user(...): ...

而不是输入

@permission_required(Permission.ADMINISTER)
def add_user(...): ...

现在,你的方式......

def permission_required(permission):
    def wrap(function):
        @functools.wraps(function)
            def wrapped_func(*args, **kwargs):
                if not current_user.can(permission):
                    abort(403)
                return function(*args, **kwargs)
            return wrapped_func
    return wrap

实际上只是写同一件事的另一种方式。从wrap返回permission_required会隐式创建闭包对象。它可以像函数一样调用,当你这样做时调用wrap。它会记住传递给permission的{​​{1}}的值,以便permission_required可以使用它。这正是我上面所展示的课程所做的。 (实际上,像C ++和Rust这样的编译语言经常通过将它们变成类似我所展示的类定义来实现闭包。)

请注意wrap本身也做同样的事情!我们可以进一步扩展它......

wrap

或者我们可以使用class permission_check_wrapper: def __init__(self, function, permission): self.function = function self.permission = permission functools.update_wrapper(self, function) def __call__(self, *args, **kwargs): if not current_user.can(permission): abort(403) return function(*args, **kwargs) class permission_required: def __init__(self, permission): self.permission = permission def __call__(self, function): return permission_check_wrapper(self.permission, function) 完成整个工作:

functools.partial

def permission_check_wrapper(*args, function, permission, **kwargs): if not current_user.can(permission): abort(403) return function(*args, **kwargs) def wrap_fn_with_permission_check(function, *, permission): return functools.update_wrapper( functools.partial(permission_check_wrapper, function=function, permission=permission), wrapped=function) def permission_required(permission): return functools.partial(wrap_fn_with_permission_check, permission=permission) 定义为desugar到@decorator(a,b,c) def foo的美妙之处在于,该语言并不关心您选择的这些实现技巧中的哪一种。