我叫骆驼 会点儿代码,会点儿读书 这世上的书浩如烟海 我能做的就是尽量整理分享给你 起 在上一节Flask项目实战第一弹中我们讲到了路由,先看下面代码回顾一下:from flask import Flask app = Flask(__name__) @app.route("/hello") def hello(): return "Hello World ..." if __name__ == "__main__": app.run(load_dotenv=True) @app.route("/hello") 就是装饰器。交流群里小伙伴问我,Python里的装饰器该怎么理解,今天我们好好唠唠这个东西。承 说到装饰器,我们不得不谈一个知识点:闭包。我们从代码入手,一点一点来说闭包。 Python 有一个好玩的地儿,就是 def 函数(exterior)内部可以嵌套另一个 def 函数(interior)。调用 exterior 时,若遇到 interior , 仅仅完成对于 interior 的定义,而不去运行 interior。如果 exterior return interior,那么我们可以使用 interior () 去调用 内部函数 interior 函数。 var = 0 def exterior(): var = 1 def interior(): print(var) return interior() # 这里返回 interior 函数调用结果 exterior() # 打印 1 从上面代码和结果中可以看到,interior 打印的 var 值 并非 第一行的 var。这说明,exterior 中的嵌套变量 var 覆盖了全局变量var=0,然后 interior 中的本地变量按照引用规则,就引用了var = 1。 接下来,我们仔细想想下面这句话: interior 作用域在函数结束后就立即失效,而exterior嵌套作用域在 interior 的函数返回后却仍然有效。 var = 0 def exterior(): var = 1 def interior(): print(var) return interior # 这里返回 interior 函数对象 inter = exterior() inter() # 打印 1 看完上面代码,再思考一下刚刚的话。如果还不清楚,看下图: 图解 创建一个闭包必须满足以下几点:必须有一个内嵌函数内嵌函数必须引用外部函数中的变量外部函数的返回值必须是内嵌函数转 现在有了闭包的知识点,我们再聊聊装饰器(decorator)。我要掰开了揉碎了来说说装饰器。 刚刚接触装饰器的同学会对这个概念感到迷茫,然后你在网上(尤其 csdn)找例子或者教程,基本千篇一律,或者讲解的"点到为止",你在看完之后,或许更迷茫了。 函数是什么 在说装饰器前,我们聊聊 Python 的函数。众所周知:在 Python 中,一切皆对象,函数是一等对象。 编程语言理论家把"一等对象"定义为满足下述条件的程序实体:在运行时创建①能赋值给变量或者数据结构中的元素②能作为参数传递给函数③能作为函数的返回结果④ 我们看这么一段程序:def double(x: int) -> int: return x * 2 这段代码很简单,计算了一个整数的2倍。那么我么用 dis 模块进行反编译,看看他是怎么运行的。>>>from my_test import double >>>from dis import dis >>>dis(double) # 结果如下 源码行号 指令在函数中的偏移 指令符号 指令参数 实际参数值 2
0
LOAD_FAST 0
x 2
LOAD_CONST 1
2
4
BINARY_MULTIRLY 6
RETURN_VALUE 指令符号解释:LOAD_FAST :一般加载局部变量的值,也就是读取值,用于计算或者函数调用传参等; LOAD_CONST :加载 const 变量,比如数值、字符串等等; BINARY_MULTIRLY:见名知意,二进制乘法RETURN_VALUE:返回值 结合反编译的结果,仔细理解一下代码的运行流程。下面我们看另外一个例子:def double(x: int) -> int: return x * 2 def triple(x: int) -> int: return x * 3 def call_func(func, x: int) -> int: return func(x) result = call_func(triple, 2) print(result)dis(call_func) 源码行号 指令在函数中的偏移 指令符号 指令参数 实际参数值 10
0
LOAD_FAST 0
func 2
LOAD_FAST 1
x 4
CALL_FUNCTION 1
6
RETURN_VALUE 在运行过程中:出现了 CALL_FUNCTION 。结合第13行代码,仔细体会一下这句话:函数能作为参数传递给另外一个函数。 我们现在看一下闭包的执行流程def call_func(): def double(x: int) -> int: return x * 2 return double dis(call_func) 里边出现了一个关键词:MAKE_FUNCTION,见名知意,创建函数。此时再回想"一等对象"所满足的条件。 说了这么多,无非是想告诉大家一个重要的东西,函数就是对象,可以被另一个函数返回,可以被赋值,也可以被调用。 其实到这里,才真是说完闭包这个东西。装饰器和闭包大同小异,下面我们接着来。 装饰器 有这种一种等价语法:def callfunc(func): return 1 @callfunc def triple(x: int) -> int: return x * 3 等价于def callfunc(func): return 1 def triple(x: int) -> int: return x * 3 triple = callfunc(triple) 无论上面那种方式,我们输出的 tripre 这个对象的值都是 1>>> print(triple) >>> 1 所以,闭包可以写成@这种形式呢?其实,装饰器可以理解为闭包的一种,我们可以这样认为:闭包传递的是变量,而装饰器传递的是函数,除此之外没有任何区别。 我们看一个打印时间的装饰器:import time def timeit(func): def wrapper(x): start = time.time() ret = func(x) print(time.time() - start) return ret return wrapper @timeit def my_func(x): time.sleep(x) my_func(1) timeit 装饰器就打印 my_func 函数的运行时间。是不是在了解完闭包之后很简单了。 装饰器的作用就是:在不改变原函数的情况下,对已有函数进行额外的功能扩展。 恭喜你,Python 技能又进一步。 回到 Flask 我们看看路由装饰器 Flask 中路由的装饰器很简单,我们以 route 为例,以下是 route 函数源码(抽离版):import typing as t def add_url_rule(rule, endpoint, f, param): pass def route(rule: str, **options: t.Any) -> t.Callable: def decorator(f: t.Callable) -> t.Callable: endpoint = options.pop("endpoint", None) add_url_rule(rule, endpoint, f, **options) return f return decorator route 函数就是一个装饰器,内部嗲用 add_url_rule 实现真正的路由添加。再回过头看看装饰器的作用和定义以及使用,是不是明白了许多!加油,慢就快,快就是慢。合 我整理了自学 Python 的视频,涉及到 爬虫、Web、数据分析、机器学习和深度学习等内容,留言转发并后台发送:"你好,Python",就可以免费获取啦! 爬虫 我也是从大学一路自学走来的,深知自学的情况下,没有项目是多么的难受。现在免费赠送python项目实战,后台私聊即可获得。 最后,动动手点个赞,您的支持是我创作的最大动力[比心]