Python中的装饰器是一个特殊的函数,用于不改变原函数定义的情况下为原函数增加额外功能.
尝试理解:
如果原函数是手机屏,装饰器可以被看作是贴的膜.
- 贴什么膜,贴多少膜不会影响原屏幕显示的内容,也不会影响手机的运行
- 手机屏上有些东西是不希望因为贴膜被覆盖(比如摄像头,听筒)或改变(LOGO),所以膜会用挖孔或复制的方式处理.
- 如果你有多种需求时,你可以贴多张膜,也可以贴一张复合膜(钢化防窥膜)
- 膜可以买回来不贴,但只能贴在可贴的地方
- 膜和被贴对象是连在一起的,如果贴多张膜,最先贴的最靠近屏,最后贴的最先被看到.
基本用法:
最简单的用法
def decorator1(func): #定义装饰器
….
Return func #装饰器返回一个函数
@decorator1 #使用装饰器
def function1(): #原始函数
…
多次装饰用法
def decorator1(): #定义装饰器
….
Return func #装饰器返回一个函数
def decorator2(): #定义装饰器
….
Return func #装饰器返回一个函数
@decorator1 #再把下面生成的函数装进装饰器
@decorator2 #原函数装进装饰器,生成同名函数
Def function1(): #原始函数
…
复合装饰用法
def decorator 1(text):
def decorator2(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@decorator1(‘xxx’) #注意这里,相当于newDecorator= decorator1(‘xxx’) @newDecorator
def function1(): #原始函数
…
实例详解
小学水平-低年级
def decorator (func):
print('装饰器开始执行')
return func
@decorator
def fn1(x,y):
print('原函数开始执行')
return x*y
print('定义完了,准备执行')
print('fn1的执行结果为:',fn1(3,4))
----------------以下执行结果---------------
装饰器开始执行
定义完了,准备执行
原函数开始执行
fn1的执行结果为:12
可以很明显的看出@decorator
时,相当于执行了fn1=decorator(fn1)
准确的说是
tmp = decorator(fn1)
fn1 = tmp
小学水平-中年级
def decorator1 (func):
print('第一个装饰器开始执行')
return func
def decorator2 (func):
print('第二个装饰器开始执行')
return func
@decorator1
@decorator2
def fn1(x,y):
print('原函数开始执行')
return x*y
print('定义完了,准备执行')
print('fn1的执行结果为:',fn1(3,4))
----------------以下执行结果---------------
第二个装饰器开始执行
第一个装饰器开始执行
定义完了,准备执行
原函数开始执行
fn1的执行结果为: 12
可以看出,最靠近原函数的最先执行,这和贴膜一样啊:
@decorator2
时,相当于执行了fn1=decorator2(fn1)
@decorator1
时,相当于执行了fn1=decorator1(@decorator2)
即:
fn1=decorator1(decorator2(fn1)))
但是,为啥decorator2中的print语句先执行了,而不是先遇到的decorator1中先执行呢?如何理解理顺这种执行顺序呢?
前面说了,装饰器执行时,会将跟在后面的函数送到装饰器里,生成一个新函数,并赋值给原函数名这个变量.所以要把 修饰语句和函数定义做为一个整体来看:
就像贴膜后的手机,你看到的是最外的膜,但执行贴膜动作时是从最里面的膜开始贴
所以@decorator1
时,会要求@decorator2
,即原函数被decorator2处理后的结果
小学水平-高年级
前面的装饰器,都是仅将原函数作为参数输入,然后输出装饰后的函数.
现在的情况是想把几个装饰器整合起来,通过参数来控制装饰结果,那要怎么做呢?
即要实现以下形式:
def decorator(avg):
....
@decorator(avg)
def func(x,y):
....
很容易看出,要让程序可以正常执行,我们不但要让decorator返回的不但是一个函数,还得是个装饰器函数,重要的是这个返回的函数是要和原函数一样可以接收两个参数的.
试着把前面中年级的题目改写一下:
def decorator1 (avg):
print('第一个装饰器开始执行,','输入参数为:',avg)
def decorator2 (func):
print('第二个装饰器开始执行')
def func3(*args,**kw): #这段的作用就在于让第二个装饰器来接收原函数的参数
print('func3开始执行了')
return func(*args,**kw)
return func3
return decorator2
@decorator1('hello')
def fn1(x,y):
print('原函数开始执行')
return x*y
print('定义完了,准备执行')
print('fn1的执行结果为:',fn1(3,4))
----------------以下执行结果---------------
第一个装饰器开始执行, 输入参数为: hello
第二个装饰器开始执行
定义完了,准备执行
func3开始执行了
原函数开始执行
fn1的执行结果为: 12
再来个例子,为函数增加写日志的装饰器,并对不同函数标注不同级别
def logging(level):
def wrapper(func):
def inner_wrapper(*args, **kwargs):
print ("[{level}]: enter function {func}()".format(level=level,func=func.__name__))
return func(*args, **kwargs)
return inner_wrapper
return wrapper
@logging(level='INFO')
def say(something):
print ("say {}!".format(something))
say('哈罗')
print(say.__name__)
----------------以下执行结果---------------
[INFO]: enter function say() 注意此处INFO即装饰器参数
say 哈罗!
inner_wrapper
这就是一个复合膜的例子,不过做这个膜的厂家偷懒了,居然把手机LOGO给贴住了.
我们看到封装后的函数名被改了,所以用内置的functools.wraps 装饰器处理一下
import functools #引入functools
def logging(level):
def wrapper(func):
@functools.wraps(func) #在这儿,把匹配原函数的新函数名改成原函数
def inner_wrapper(*args, **kwargs):
...
----------------以下执行结果---------------
[INFO]: enter function say()
say 哈罗!
say <== 函数名回来了