在你的问题中,你已经命名了两者,检查user_id
,以及想要的装饰器has_permissions
,所以我将举一个名称更清晰的示例:让我们创建一个装饰器,当颜色(字符串)为'green'
.
Python 装饰器是函数工厂
装饰器本身(if_green
在我下面的示例中)是一个函数。它需要一个函数来装饰作为参数(名为function
在我的示例中)并返回一个函数(run_function_if_green
在示例中)。通常,返回的函数在某个时刻调用传递的函数,从而用它之前或之后或两者都运行的其他操作来“装饰”它。
当然,它可能只是有条件地运行它,正如您似乎需要的那样:
def if_green(function):
def run_function_if_green(color, *args, **kwargs):
if color == 'green':
return function(*args, **kwargs)
return run_function_if_green
@if_green
def print_if_green():
print('what a nice color!')
print_if_green('red') # nothing happens
print_if_green('green') # => what a nice color!
当你用装饰器装饰一个函数时会发生什么(就像我所做的那样)print_if_green
,这里)是装饰器(函数工厂,if_green
在我的示例中)被原始函数调用(print_if_green
正如您在上面的代码中看到的那样)。正如它的本质一样,它返回一个不同的函数。那么Pythonreplaces原始函数与装饰器返回的函数。
所以在后续的调用中,它是返回的函数(run_function_if_green
与原来的print_if_green
as function
)被称为print_if_green
并且有条件地进一步调用原始的print_if_green
.
函数工厂可以生成带参数的函数
对装饰器的调用(if_green
) 对于每个修饰函数仅发生一次,而不是每次调用修饰函数时发生。但随着函数returned由装饰者一次性永久地replaces原始函数,每次调用原始函数时都会调用它而不是原始函数。如果我们允许的话,它可以接受争论。
我已经给了它一个论点color
,它使用自身来决定是否调用装饰函数。此外,我给了它惯用的 vararg 参数,它用它来调用包装函数(如果它调用它),这样我就可以使用任意数量的位置参数和关键字参数来装饰函数:
@if_green
def exclaim_if_green(exclamation):
print(exclamation, 'that IS a nice color!')
exclaim_if_green('red', 'Yay') # again, nothing
exclaim_if_green('green', 'Wow') # => Wow that IS a nice color!
装饰函数的结果if_green
是一个新的第一个参数被添加到其签名之前,这对于原始函数来说是不可见的(如run_function_if_green
不转发)。由于您可以自由地实现装饰器返回的函数,因此它还可以使用更少、更多或不同的参数调用原始函数,在将它们传递给原始函数之前对它们进行任何所需的转换或执行其他疯狂的操作。
概念、概念、概念
了解装饰器需要了解和理解 Python 语言的各种其他概念。 (其中大多数不是 Python 特有的,但人们可能仍然不知道它们。)
为了简洁起见(这个答案已经足够长了),我跳过或掩盖了其中的大多数。为了更全面地快速浏览(我认为)所有相关的内容,请参阅例如通过 12 个简单步骤了解 Python 装饰器!.