python中的语法糖, 装饰器, 需要先搞清楚闭包和函数参数解包
装饰器
装饰器要装饰一个对象, 可以是函数, 可以是类, 装饰器本身可以是函数也可以是类
装饰
- 能够接受一个对象
- 要有对对象的修改或利用对象的能力
- 对象的基本功能不能变(装饰不应该改变我们写出的直观的功能)
- 装饰器可能还需要我们能够进行一定的控制, 它可能会需要参数
- 如果我们愿意, 不应该只能装饰一次
一个例子
一个函数装饰函数的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import time
def de(func, *args, **kwargs): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) print('%.3f s' % (time.time() - start)) return result return wrapper
@de def f(): time.sleep(3.123) print("Hi...")
f()
Hi... 3.124 s
|
能够接受一个对象
函数当然可以接受一个函数作为参数
要有对对象的修改或利用对象的能力
我们当然可以在装饰器内做些事情, 比如计算运行时间
这还是可以应在任意函数的装饰器, 我们还可以为特定的函数编写装饰器, 比如flask将视图函数与路由连接起来
对象的基本功能不能变
正如我们看到的,
- 将所有的参数获取并传给原来的函数
- 我们返回一个函数
什么也不做的装饰器
1 2 3 4 5 6 7 8
| def de(func, *args, **kwargs): def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper
def de(func): return func
|
我们需要参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| def de2(s): def de(func): def wrapper(*args, **kwargs): start = time.time() time.sleep(s) result = func(*args, **kwargs) print('%.3f s' % (time.time() - start)) return result return wrapper return de
@de2(2) def f2(): print("Hi.....")
f2()
Hi..... 2.001 s
|
f2 = de2(s)(f2)
de2(s)返回了一个函数de, 即de2(s)=de, 即我们没有参数时的装饰器
de(f2), 返回了我们的函数
应该记住的
最后返回的是最内层的wrapper函数, wrapper外部的函数都已执行
所以我们可以看作装饰有两部分, 一部分wrapper函数外部代码, 定义函数时即进行装饰,
另一部分是wrapper内部代码, 调用时进行装饰
不仅要原函数的返回值
1 2 3 4 5 6 7 8 9 10
| def de2(s): def de(func): def wrapper(*args, **kwargs): start = time.time() time.sleep(s) result = func(*args, **kwargs) print('%.3f s' % (time.time() - start)) return result return wrapper, time.time() - start return de
|
多个装饰器
离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰
我们最好不要写……会晕的
类装饰器
类装饰器依赖的是类的__call__
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class De(): def __init__(self, func): self.s = "Hi!" self.func = func
def __call__(self, *args, **kwargs): print(self.s) return self.func(*args, **kwargs)
@De def foo(s): print(s)
foo("Hi...")
Hi! Hi...
|
使用场景
通用装饰器
就像例子中的装饰器, 用在那个函数上都可以
其他
- 将函数余其他东西关联,
- 原函数调用前检查
- 原函数调用时检查
- 函数调用后的一些处理
注意
原函数的属性会变
例, 函数也是个对象, 它的属性__name__
为函数名, 但是装饰器工作后会改变它的属性, 按照我们的等价写法来理解, 这很正常.
解决
引起我们函数名改变的原因就是wrapper
的调用, 所以我们可以手动修改wrapper.__name__=func.__name__
更好的方法是使用functools
库的wraps
装饰器
1 2 3
| @functools.wraps(func): def wrapper(*arg, **kwargs): ...
|
这样使用装饰器不会有任何副作用
模板
有时我们直接记住一些模板就好了, 甚至我们都不会取写装饰器, 最少应该在自己使用时了解它工作的过程
函数装饰函数
1 2 3 4 5 6 7 8 9
| def de(cls): ... def wrapper(*arg, **kwargs): ... result = cls(*arg, **kwargs) ... return result[, ...] ... return wrapper
|
函数装饰类
1 2 3 4 5 6 7 8 9
| def de(cls): ... def wrapper(*arg, **kwargs): ... instance = cls(*arg, **kwargs) ... return instance[, ...] ... return wrapper
|
类装饰函数
1 2 3 4 5 6 7 8 9 10 11 12
| class De(): def __init__(self, func): self.func = func ... ...
def __call__(self, *args, **kwargs): ... result = self.func(*args, **kwargs) ... return result[, ...]
|
类装饰类
1 2 3 4 5 6 7 8 9 10 11 12
| class De(): def __init__(self, cls): self.cls = cls ... ...
def __call__(self, *args, **kwargs): ... instance = self.cls(*args, **kwargs) ... return instance[, ...]
|