【问题标题】:creating decorator out of another decorator (python)从另一个装饰器(python)创建装饰器
【发布时间】:2017-11-16 19:10:48
【问题描述】:

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

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

@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 的装饰器的返回语句。我不知道这是如何工作的。主要是我们返回原始装饰器+函数的return语句(语法很奇怪)。有人可以详细说明一下吗? - 非常欢迎详细信息

【问题讨论】:

  • 我发现将 decorator-that-takes-args 视为装饰器 factory 很有帮助。你向工厂传递了一些参数,它返回一个函数,它是真正的装饰器,而那个真正的装饰器将你正在装饰的函数作为 its 唯一的参数。

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


【解决方案1】:

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

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

应该是:

bye = decorator(*args)(bye)

或者也许你会发现更清晰:

temp = decorator(*args)
bye = temp(bye)

(当然,实际上没有创建temp 变量。)

在您的第二期中,@admin_required 被定义为@permission_required(Permission.ADMINISTER) 的快捷方式。

【讨论】:

    【解决方案2】:

    当参数以装饰符号给出时,

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

    它是写作的语法糖

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

    也就是说,decorator 使用参数 a、b、c 调用,然后它返回的对象 使用唯一参数 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
    

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

    注意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)
    

    @decorator(a,b,c) def foo 定义为去糖为foo = decorator(a,b,c)(foo) 的美妙之处在于,该语言并不关心您选择这几种实现技术中的哪一种。

    【讨论】:

      猜你喜欢
      • 2011-09-03
      • 2015-03-28
      • 1970-01-01
      • 2014-02-13
      • 2018-06-21
      • 2015-02-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多