欢迎来到 Python!这是很多很棒的问题。让我们一次拿一个。此外,只是一个公平的警告点。这个话题让你头晕目眩,然后才全部合二为一。
作为参考,这是您的示例装饰器和被装饰的函数:
# Decorator Function
def login_required(something):
@wraps(something)
def wrap(*args, **kwargs):
if "some_admin_name" in session:
return something(*args, **kwargs)
else:
flash("\"You shall not pass!\" - Gandalf")
return redirect(url_for("login"))
return wrap
# Function Being Decorated
@app.route("/some/restricted/stuff")
@login_required
def main():
return render_template("overview.html",
stuff = getstuff() )
“某事”的论据是什么?是这个要求吗?!
要回答这个问题,我们首先要回答什么是装饰器。根据您正在装饰的对象类型,答案可能会有所不同。在这种情况下,当您正在装饰一个函数时,您可以将装饰器视为一种方法/函数,它允许程序员修改另一个函数的行为。
有了这个,我们可以回答你的问题。“某物”是您要装饰的功能/方法。 是的,它是一个将另一个函数作为参数的函数。
让我们更改装饰器函数的语言以使其更清晰:
def login_required(function_to_wrap):
@wraps(function_to_wrap)
def wrap(*args, **kwargs):
if "some_admin_name" in session:
return function_to_wrap(*args, **kwargs)
else:
flash("\"You shall not pass!\" - Gandalf")
return redirect(url_for("login"))
return wrap
args 和 kwargs 是什么?
简短的回答是,这是 Python 允许参数程序员编写采用可变数量的关键字和非关键字参数的函数/方法的方式。
通常,在编写函数时,会显式指定参数。例如:
def add_these_numbers(number_1, number_2):
return number_1 + number_2
然而,这并不是唯一的做事方式。你也可以使用 *args 或 **kargs 来完成同样的事情:
def add_these_numbers(*args):
return args[0] + args[1]
def add_these_numbers_too(**kwargs):
return kwargs['first_number'] + kwargs['second_number']
因为它与您的问题有关,*args/**kwargs
所以通常在装饰器中使用,因为装饰器通常应用于各种方法,这些方法将采用各种参数。
Usingargs/**kwargs
允许您的装饰器方法通过装饰器函数传递该方法最初需要的方法。 如果这让你头晕目眩,请告诉我,我会尽力澄清。
让我们更改 main() 以便更清楚:
# Function Being Decorated
@app.route("/some/restricted/stuff")
@login_required
def main(html_template):
return render_template(html_template, stuff = getstuff())
为什么我必须在方法内包装一个方法才能将其用作装饰器?
在我看来,这是理解装饰器最棘手的部分。 关键是要理解,装饰器的核心是接管原始函数的名称。
理解这一点的最简单方法是应用装饰器而不使用方便的 @ 语法。以下是等价的:
@login_required
def main():
....
main = login_required(main)
抓住你的马,这就是令人敬畏的地方! 这两个代码片段告诉 Python 的是,“'main' 这个词应该不再指代 main() 函数,而是当它作为参数传递给原始 main() 函数时的结果 login_required() 函数。
哇!?
是的。对 main() 的调用现在指的是对 login_required(main()) 的调用结果。这也是 login_required 返回嵌套函数的原因。新的 main() 必须仍然是一个函数,就像旧的一样。
不同的是,现在新的 main 函数实际上是 wrap() 的一个实例,由传递给 login_required() 的参数自定义。
所以...实际上 main() 现在等效于以下内容:
def main(*args, **kwargs):
if "some_admin_name" in session:
return predecorator_main_function(*args, **kwargs)
else:
flash("\"You shall not pass!\" - Gandalf")
return redirect(url_for("login"))
这仅适用于 Flask 吗?还有其他类似的情况可以派上用场吗?
当然不!装饰器是 Python 内置的众多超棒功能之一。当您不想创建其他函数以避免代码重复时,装饰器在您想要对现有函数/方法进行修改(我认为相对较小)的任何情况下都很有用