Python 中的装饰器是通过利用了函数特性的闭包实现的, 所以在讲装饰器之前, 我们需要先了解函数特性, 以及闭包是怎么利用了函数特性的
1 函数特性
python 中的函数特性总的来说有以下四点:
1. 函数作为变量传递
- def add(x):
- return x + 1
- a = add # 作为变量
说明: 函数如果不加括号, 是不会执行的, 代表的是一个函数对象, 它是可以作为变量来传递
2. 函数作为参数传递
- def add(x):
- return x + 1
- def execute(f):
- return f(3)
- execute(add) # 作为参数
说明: 一个函数可以接受另一个函数对象作为自己的参数, 并对函数对象进行处理
3. 函数作为返回值
- def add(x):
- return x + 1
- def get_add():
- return add # 作为返回值
说明: 一个函数的返回值可以是另一个函数对象
4. 函数嵌套及跨域访问
- def outer():
- x = 1
- def inner():
- print(x) # 被嵌套函数 inner 内部的 x 变量可以到封装域去获取
- inner()
- outer()
说明: 一个函数 (主函数) 内部是可以嵌套另一个函数 (子函数) 的, 比如 outer 函数从内部嵌套了 inner. 一个函数本地域没有的变量, 是可以跨到它的封装域 (主函数与子函数之间的范围) 去寻找的
2 闭包的实现
python 中的装饰器是通过闭包实现的, 简单地讲, 闭包就是引用了外部变量的内部函数, 而闭包的实现正是利用了以上函数特性, 下面我们来看看闭包是如何实现的:
- def outer(x):
- def inner(): # 函数嵌套
- return x # 跨域访问, 引用了外部变量 x
- return inner # 函数作为返回值
- closure = outer('外部变量') # 函数作为变量赋给 closure
- print(closure()) # 执行闭包
执行结果:
外部变量
说明: 我们分析下这个流程, outer 接收到'外部变量', 传给 inner, 作为它 return 的参数, 最后 outer 返回 inner 函数, 返回的 inner 函数作为变量传递给 closure, 最后执行 closure 这个函数对象, 实际上是执行了 inner 这个函数, 返回了 '外部变量', 这样就实现了一个简单的闭包
我们发现上面的闭包例子只用到了之前说的其中 3 个函数特性, 函数作为参数 这个特性好像并没用上, 别急, 我们一步步来, 试想一下, outer 的参数 x 是不是也可以是一个函数对象?
下面我们来改写一下实现闭包的代码:
- def func():
- return '函数 func'
- def outer(x):
- def inner(): # 函数嵌套
- return '戴了 inner 牌帽子的' + x() # 跨域访问, 引用了外部变量 x
- return inner # 函数作为返回值
- closure = outer(func) # 函数 func 作为 outer 的参数; 函数作为变量赋给 closure
- print(func()) # 执行原始函数
- print(closure()) # 执行闭包
执行结果:
函数 func
戴了 inner 牌帽子的 函数 func
说明: 我们看到打印的结果, 从 func() 到 closure(), 我们是不是感觉函数 func 被装饰了一番, 变成了 closure, 具体是怎么装饰的呢?
划重点来了!!!!!!!!!!!
我们看到 closure 实际上是 outer(func),func 作为参数传进 outer,outer 的子函数 inner 对 func 返回的结果进行了一番装饰, 返回了一个装饰后的结果, 最后 outer 返回 inner, 可以说 inner 就是装饰后的 func, 这就是一个函数被装饰的过程, 重点在于执行 outer(func) 这个步骤
3 装饰器语法糖 @
python 给我们提供了语法糖 @, 我们想执行 outer(func) 的时候, 只需要把 outer 函数 @到 func 函数的上面就可以了
具体实现如下:
- def outer(x):
- def inner():
- return '戴了 inner 牌帽子的' + x()
- return inner
- @outer
- def func():
- return '函数 func'
- print(func())
执行结果:
戴了 inner 牌帽子的 函数 func
说明: 我们看到打印的结果跟我们执行 closure()的结果是一样的, 也就说明 加了 outer 装饰器的 func 等价于 outer(func), 所以我们很清楚地知道装饰器 @的作用是什么了, 就是拿来把被装饰的函数作为参数传递到装饰器函数里面加工的, 最后执行被装饰函数的时候, 就相当于执行了一个加工后的函数.
以上就是 Python 中装饰器的诞生过程......
来源: https://juejin.im/entry/5b9f4ba6e51d450e9e43fbfa