在花了几个小时讨论 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 的装饰器的返回语句。我不知道这是如何运作的。主要是 return 语句,我们返回原始装饰器+函数(以奇怪的语法)。有人可以详细说明一下吗? - 非常欢迎详细信息
当参数以装饰器表示法给出时,
@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 NotImplementedError # 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
隐式创建一个closure 对象。它可以像函数一样被调用,当你这样做时,它会调用
wrap
。它会记住传递给
permission
的
permission_required
的值,以便
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)
的美妙之处在于,该语言并不关心您选择这几种实现技术中的哪一种。
@decorator(*args)
def bye():
return "bye"
将是:
bye = decorator(*args)(bye)
或者也许你会发现更清楚:
temp = decorator(*args)
bye = temp(bye)
(当然,除了实际上没有创建
temp
变量。)在第二期中,
@admin_required
被定义为
@permission_required(Permission.ADMINISTER)
的快捷方式。