这个问题可以参考廖老师的文章,很详细了,此处只简单举例,不做详细介绍。
文章链接:
https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584
def log(f):
def wrapper(*args, **kw):
print("log")
return f(*args, **kw)
return wrapper
@log
def test():
print("Just a test.")
以上面的代码为例,我们使用装饰器时用了@log
。在运行时,此处相当于执行了test = log(test)
,如果将@log
改为@log("2020")
, 那么此处就相当于执行了test = log("2020")(test)
。可以简单理解为程序将@后面的语句拿出来,添加(func_name)后再赋给原函数变量。
要注意的是,后面的部分是真的执行了,如上面的代码就是执行了log(test)
。
再如flask中,通过app.route
注册视图函数:
@app.route("/index")
def index():
...
通过这段代码,程序会自动注册index,我们访问/index时程序会自动执行index函数中的内容,并不需要额外的操作。那么注册的代码是在什么时候运行的呢?
还是之前说过的,程序运行到此处,相当于开始执行index = app.route("/index")(index)
,在执行过程中完成了注册。
看下app.route
的源码:
# app.route
def route(self, rule, **options):
def decorator(f):
endpoint = options.pop("endpoint", None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
可以发现,执行app.route("/index")(index)
时,执行了decorator(index)
, 在decorator
注册了视图函数f,并将f直接返回。所以此处执行index = app.route("index")(index)
后,index
函数并未改变。
django中有定义好的一个装饰器,叫login_required
。通过它,可以设置对应视图函数在登陆后才能访问。那么在flask中我们如何手动实现它?
flask中的视图函数都有了app.route
装饰器,再添加一个装饰器要放到@app.route
上面还是下面?
我们测试下:
def outter(f):
def wrapper(*args, **kw):
print("outter")
return f(*args, **kw)
return wrapper
def inner(f):
def wrapper(*args, **kw):
print("inner")
return f(*args, **kw)
return wrapper
@outter
@inner
def test1():
print("test1")
def test2():
print("test2")
print("test1:", test1)
print("------")
print("outter(inner)(test2): ", outter(inner)(test2))
print("------")
print("outter(inner(test2)): ", outter(inner(test2)))
运行结果如下:
test1: .wrapper at 0x01B7E340>
------
outter
outter(inner)(test2): .wrapper at 0x01B7E4F0>
------
outter(inner(test2)): .wrapper at 0x01B7E2B0>
这里用test2是因为test1已经被改变了。
从结果可以看出,多个装饰器间的执行顺序是从内到外的,先执行inner(test2)
,再将inner(test2)
的结果作为outter
的参数。
在这里我们简单的实现下flask的login_required
装饰器。
我们要实现登录认证,可以从session中获取用户的登录信息,如果获取到就继续,否则就跳转到登录页:
import functools
def login_required(f):
@functools.wraps(f)
def wrapper(*args, **kw):
if session.get("username", None) is not None:
return f(*args, **kw)
return login()
return wrapper
使用:
@app.route("/index")
@login_required
def index():
...