python中,函数可以像变量一样当作参数传递给另一个函数, 装饰器则沿用了这一特性,在不改变既有代码的前提下,增加功能。
描述
装饰器本质上是一个 Python 函数或类,返回值也是一个函数/类对象。
原则:
- 不修改被修饰函数的源代码
- 不修改被修饰函数的调用方式
装饰器 = 高阶函数+函数嵌套+闭包
高阶函数
函数接受参数为函数名或函数返回值是函数名
| 12
 3
 4
 
 | def add(x, y, f):return f(x) + f(y)
 
 add(-5,6,abs)
 
 | 
函数嵌套
函数内部定义函数
| 12
 3
 4
 5
 6
 7
 
 | def outer():def inner():
 print("inner")
 print("outer")
 inner()
 
 outer()
 
 | 
闭包
闭包,顾名思义,就是一个封闭的包裹,里面包裹着自由变量,就像在类里面定义的属性值一样,自由变量的可见范围随同包裹,哪里可以访问到这个包裹,哪里就可以访问到这个自由变量。
通常函数被调用后,局部变量变失去作用域,闭包可以避免使用全局变量,使得变量脱离了函数本身的作用范围,局部变量依旧可以被访问得到
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | def adder(x):def wrapper(y):
 return x + y
 return wrapper
 
 adder5 = adder(5)
 
 adder5(10)
 
 adder5(6)
 
 | 
简单装饰器
函数进入和退出时 ,被称为一个横切面,这种编程方式被称为面向切面的编程。在java    Spring框架aop就是面向切面编程
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | import time
 def timmer(func):
 def wrapper(*args,**kwargs):
 start_time=time.time()
 res=func(*args,**kwargs)
 stop_time = time.time()
 print(f'运行时长{stop_time-start_time}')
 return res
 return wrapper
 
 def IO_operation():
 time.sleep(4)
 
 IO_operation = timmer(IO_operation)
 IO_operation()
 
 | 
@ 语法糖 简化调用
| 12
 3
 4
 5
 
 | @timmerdef IO_operation():
 time.sleep(4)
 
 IO_operation()
 
 | 
带参数的装饰器
装饰器的语法允许我们在调用时,提供其它参数,它实际上是对原有装饰器的一个函数封装,并返回一个装饰器.
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | def timmer(level):def decorator(func):
 def wrapper(*args,**kwargs):
 start_time=time.time()
 res=func(*args,**kwargs)
 stop_time = time.time()
 if level=1:
 print(f'1:{stop_time-start_time}')
 if level=2:
 print(f'2:{stop_time-start_time}')
 return res
 return wrapper
 return decorator
 
 def IO_operation():
 time.sleep(4)
 
 
 @timmer(1)
 def IO_operation():
 time.sleep(4)
 
 IO_operation()
 
 | 
类装饰器
类装饰器主要依靠类的__call__方法,当一个类如果实现了__call__方法,那么该类的实例对象的行为就是一个函数,是一个可以被调用(callable)的对象。
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | class Add:def __init__(self, n):
 self.n = n
 def __call__(self, x):
 return self.n + x
 
 >>> add = Add(1)
 >>> add(4)
 >>> 5
 
 | 
确定对象是否为可调用对象通过内置函数callable来判断
| 12
 3
 4
 5
 6
 
 | >>> callable(foo)True
 >>> callable(1)
 False
 >>> callable(int)
 True
 
 | 
类装饰器实现
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | class Foo(object):
 def __init__(self, func):
 self._func = func
 
 def __call__(self):
 print ('class decorator runing')
 self._func()
 print ('class decorator ending')
 
 @Foo
 def bar():
 print ('bar')
 
 bar()
 
 | 
使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息被内部函数得元信息替代,python内部提供functools.wraps装饰器,它能把原函数的元信息拷贝到装饰器里面的 func 函数中,这使得装饰器里面的 func 函数也有和原函数 foo 一样的元信息了。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | from functools import wrapsdef logged(func):
 @wraps(func)
 def with_logging(*args, **kwargs):
 print func.__name__
 print func.__doc__
 return func(*args, **kwargs)
 return with_logging
 
 @logged
 def f(x):
 """does some math"""
 return x + x * x
 
 | 
装饰器顺序
一个函数还可以同时定义多个装饰器,比如:
| 12
 3
 4
 5
 6
 7
 
 | @a@b
 @c
 def f ():
 pass
 
 >>>f = a(b(c(f)))
 
 | 
参考:
https://foofish.net/python-closure.html
https://foofish.net/python-decorator.html
https://foofish.net/function-is-first-class-object.html