第八章、函数进阶之装饰器02
一、无参装饰器
1. 什么是装饰器?
装饰器指的是为被装饰对象添加功能,因此定义装饰器就是定义一个函数,只不过是该函数是用来为其他函数添加额外的功能
注意:
- 装饰器本身是可以任意调用的对象
- 被装饰的对象也可以是任意可调用了的对象
2. 为什么要用装饰器
装饰器的实现必须遵循两大原则
-
不修改被装饰的源代码
-
不修改被装饰的对象的调用方式
装饰器就是在遵循以上两个原则的前提下被装饰对象添加功能
#真正的装饰器 import time def index(): print('您好啊!') time.sleep(1) def deco(func): # func = 原index '''装饰器''' def f1(): #重新创建后的index start = time.time() func() #原index end = time.time() print(end-start) return f1 index = deco(index) index() #f1
3. 怎么用装饰器
第一种传参方式:
def index():
print('welcome to index')
time.sleep(1)
def time_count(func):
# func = 最原始的index
def wrapper():
start = time.time()
func()
end = time.time()
print(f"func time is {start-end}")
return wrapper
f = time_count(index)
f()
#index = time_count(index) # index为被装饰函数的内存地址,即index = wrapper
#index() # wrapper()
welcome to index
func time is -1.0008289813995361
下面方式是对第一种的再优化
第二种传参函数:包给函数——外包(指的是再定义命名为原函数的名字的变量,被赋予了原函数的内存地址,再调用被装饰后的函数名字(),以后就可以直接用原来函数的名字调用了)
import time
def index():
print('welcome to index')
time.sleep(1)
def time_count(func):
# func = 最原始的index
def wrapper():
start = time.time()
func()
end = time.time()
print(f"{func} time is {start-end}")
return wrapper
# f = time_count(index)
# f()
index = time_count(index) # index为被装饰函数的内存地址,即index = wrapper
index() # wrapper()
welcome to index
func time is -1.0008289813995361
4. 完善装饰器
上述的装饰器,最后调用index()(实际上是被装饰后的index())的时候,其实是在调用wrapper(),因此如果原始的index()有返回值的时候,wrapper()函数的返回值应该和index()的返回值相同,也就是说,我们**需要同步原始的index()和wrapper()方法的返回值。 **具体怎么做:
import time
def index():
print('welcome to index')
time.sleep(1)
return 123
def time_count(func):
# func = 最原始的index
def wrapper():
start = time.time()
res = func()
end = time.time()
print(f"{func} time is {start-end}")
return res
return wrapper
index = time_count(index)
res = index()
print(f"res: {res}")
#此处return就已经把原始的index的返回值接收过来了
res = func()
end = time.time()
print(f"{func} time is {start-end}")
return res
welcome to index
<function index at 0x102977620> time is -1.0050289630889893
res: 123
如果原始的home()方法需要传参,那么我们之前的装饰器是无法实现该功能的,由于有timeinner()=home(),所以给timeinner()方法传参即可。
import time
def home(name):
print(f"welcome {name} to home page")
time.sleep(1)
return name
def time_count(func):
# func = 最原始的index
def timeinner(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print(f"{func} time is {start-end}")
return res
return timeinner
home = time_count(home)#就相当于把time_count()的函数返回值(其实是内部函数的地址)赋值给另一个变量名(其实就是被装饰后的原函数的名字)
res = home('egon')#调用了timeinner("egon")
print(f"res: {res}")
5. 装饰器语法糖
被装饰的函数正上方单独写上@装饰器名
import time
def time_count(func):
# func = 最原始的index
def wrapper():
start = time.time()
func()
end = time.time()
print(f"{func} time is {start-end}")
return wrapper
@time_count
def index():
print('welcome to index')
time.sleep(1)
welcome to index
func time is -1.000335454940796
6. 装饰器模板
#双层装饰器
def deco(func):
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
return res
return wrapper
#三层装饰器
# 三层装饰器:给双层装饰器加参数
def sanceng (engine):
def outter(func):
def wrapper(*args,**kwargs): # wrapper是未来要运行的函数
#加功能
print(engine)
res = func(*args,**kwargs) #func是被装饰的函数
return res
return wrapper
return outter
@sanceng('db')
def shopping():
print('shopping')
shopping()
二、有参函数
1. 三层闭包
# 三层装饰器:给双层装饰器加参数
def sanceng (engine):
def outter(func):
def wrapper(*args,**kwargs): # wrapper是未来要运行的函数
#加功能
print(engine)
res = func(*args,**kwargs)
print(res)#func是被装饰的函数
return res
return wrapper
return outter
@sanceng('db')
def shopping(x="666"):
print('shopping')
return x
shopping()
db
shopping
666