装饰器的形成过程:
从最简单的,到有返回值的,到有一个参数的,再到万能参数。
装饰器的作用
不想修改函数的调用方式,但还想在原来的基础上添加功能,下面的timer就是一个装饰器,只是对一个函数有一些装饰的功能。
def func():
time.sleep(0.01)
print('你好!')
def timmer(f): # 装饰器函数
def inner():
start = time.time()
ret = f() # 被装饰的函数
end = time.time()
print(end - start)
return ret
return inner
func = timmer(func)
func()
注意!!!
上面代码中inner函数中,return inner一定不能再后面加括号。因为在被调用时,inner返回的是内存地址,func = timmer(func)执行时,func得到的也是inner函数的内存地址,最后func()执行时,实际上执行的时timmer函数中的inner函数。
语法糖
import time
def timmer(f): # 装饰器函数
def inner():
start = time.time()
ret = f() # 被装饰的函数
end = time.time()
print(end - start)
return ret
return inner
@timmer # 语法糖
def func():
time.sleep(0.01)
print('你好!')
# func = timmer(func)
func() # inner()
此时,相较于上个代码,加上了语法糖的修饰字符@timmer
,这个时候,就可以把func = timmer(func)
这句话省略不写。同时,在装饰函数中,要写返回值,即上面代码中的return ret
。但是要注意的是,修饰函数要写在被修饰函数的前面。请仔细对比上面两个代码的区别!!!
最后,func()
实际上运行的是inner
函数。
原则
- 开放:对扩展是开放的
- 封闭:对修改是封闭的
装饰带参数函数的装饰器
import time
def timmer(f): # 装饰器函数
def inner(*args, **kwargs):
# (1,2) /(1)
start = time.time()
ret = f(*args, **kwargs) # f(1,2) #被装饰的函数
end = time.time()
print(end - start)
return ret
return inner
@timmer # 语法糖 @装饰器函数名
def func(a, b): # 传两个参数的被装饰的函数
time.sleep(0.01)
print('你好!!', a, b)
return '新年好1'
# @timmer # 语法糖 @装饰器函数名
# def func1(a): #只创一个参数的被装饰的函数
# time.sleep(0.01)
# print('大家好',a)
# return '新年好'
# func = timmer(func)
ret = func(1, 2) # inner()
ret1 = func(1, b=6) # inner()
print(ret)
print(ret1)
输出:
装饰器函数中使用*args,**kwargs可以传入任意多个参数。
装饰器的固定模式(可能是笔试题)
def wrapper(f):
def inner(*args, **kwargs):
# 在装饰函数前要做的事
ret = f(*args, **kwargs)
# 在装饰函数之后要做的事
return ret
return inner
@wrapper # qqxing = wrapper(qqxing)
def qqxing():
print(123)
ret = qqxing() # inner
多个装饰器装饰同一个函数
有些时候,我们也会用到多个装饰器装饰同一个函数的情况。
import functools # from functools import wraps
def wrapper1(f):
@functools.wraps(f) # import functools是导入functools模块。现在,只需记住在定义wrapper()的前面加上@functools.wraps(func)即可。
def inner1():
print('wrapper1 ,before func')
ret1 = f() + '1111111111'
print('wrapper1 ,after func')
return ret1
return inner1
def wrapper2(func):
@functools.wraps(func)
def inner2():
print('wrapper2 ,before func')
ret2 = func() + '2222222222'
print('wrapper2 ,after func')
return ret2
return inner2
@wrapper1
@wrapper2
def f():
return 'in f'
ret = f()
print(ret)
print(f.__name__) # 如果前面没有@function.wraps(),就会出现inner1
输出:
因为我们讲了函数也是对象,它有__name__等属性,但你去看经过装饰器之后的函数,它们的__name__已经从原来的’f’变成了’inner’,所以我们要在装饰器最内层函数前加上@functools.wraps(func)
或者引用from functools import wraps
,然后又在修饰器前面加上@wraps(func)
就可以保持名字不变。