装饰器、开放封闭原则、语法糖、万能装饰器、带参数的装饰器、 多个装饰器装饰一个函数
装饰器
装饰器作用:在不修改函数的调用方式,但是还想在原来的函数前后添加功能
案例:测试200个函数,每个函数的运行时间是多少
# 建议进Debug模式查看运行 #需求获取一段代码得运行时间,架设需要测200个函数代码 import time # 导入时间模块 def func(): #start = time.time() # 获取当前时间 time.sleep(0.3) # 当程序执行到这里得时候,在多少秒后接着执行 print('hahaha') #end = time.time() #print(end-start) # 获取时间差 def timmer(f): # f获得了func的内存地址 f = func # 装饰器函数 def inner(): start = time.time() # 获取当前时间 f() # 调用func() # 被装饰的函数 end = time.time() print(end-start) # 获取时间差 return inner func = timmer(func) # timmer函数里的实参是需要测试的函数名 # func获得timmer函数的返回值 inner func() # 运行inner()
# 不想修改函数的调用方式,但是还想在原来的函数前后添加功能
# timmer就是一个装饰器函数,只是对一个函数有一些装饰作用
开放封闭原则
开放:对扩张是开放的,即在已有的功能环境下,新增更多的功能
# 这段代码是装饰器函数,在不修改原代码的情况下,新增功能 def timmer(f): # f获得了func的内存地址 f = func def inner(): start = time.time() # 获取当前时间 f() # 调用func() end = time.time() print(end-start) # 获取时间差 return inner
封闭:对修改是封闭的,即已经封板的程序不能再修改。
# 这段函数相当于封板的,不能再修改了,比如修改sleep会改变我整个函数 def func(): #start = time.time() # 获取当前时间 time.sleep(0.3) # 当程序执行到这里得时候,在多少秒后接着执行 print('hahaha') #end = time.time() #print(end-start) # 获取时间差
语法糖 (@)
@装饰器的函数名,同时该语法糖紧接着下行就是被装饰的函数。
import time # 导入时间模块 def timmer(f): # f获得了func的内存地址 f = func def inner(): start = time.time() # 获取当前时间 ret = f() # 调用func() 获取func()函数的返回值 end = time.time() print(end-start) # 获取时间差 return ret # 获得上面ret的值 return inner @timmer # 等同于func=timmer(func) def func(): #start = time.time() # 获取当前时间 time.sleep(0.3) # 当程序执行到这里得时候,在多少秒后接着执行 print('hahaha') #end = time.time() #print(end-start) # 获取时间差 # func = timmer(func) # timmer函数里的实参是需要测试的函数名 # a获得timmer函数的返回值 inner ret = func()
进阶,装饰带函数的装饰器
import time # 导入时间模块 def timmer(f): # f获得了func的内存地址 f = func def inner(*args,**kwargs): # 动态参数,可以接受任意参数 start = time.time() # 获取当前时间 ret = f(*args,**kwargs) # 调用func() # 动态参数,可以接受任意参数 end = time.time() print(end-start) # 获取时间差 return ret return inner @timmer # 等同于func=timmer(func) def func(a,b): # 传参 #start = time.time() # 获取当前时间 time.sleep(0.3) # 当程序执行到这里得时候,在多少秒后接着执行 print('hahaha',a,b) #end = time.time() #print(end-start) # 获取时间差 # func = timmer(func) # timmer函数里的实参是需要测试的函数名 # a获得timmer函数的返回值 inner ret = func(1,2) # inner() #传参
万能装饰器---装饰器的固定写法
#装饰器的固定写法 def wrapper(f): # 装饰器含函数,f是被装饰的函数 def inner(*args,**kwargs): '''在被装饰函数之前要做的事''' ret = f(*args,**kwargs) # 被装饰的函数 '''在被装饰函数之后-要做的事''' return ret return inner
@wrapper # test = wrapper(test)
def test():
print(234)
ret = test()
进阶,显示函数名
from functools import wraps # 导入已经写好的装饰器 作用是使用__name__打印函数名的时候 直接显示对应的函数名 def wrapper(f): # 装饰器含函数,f是被装饰的函数 @wraps(f) # 给函数inner进行装饰 def inner(*args,**kwargs): '''在被装饰函数之前要做的事''' ret = f(*args,**kwargs) # 被装饰的函数 '''在被装饰函数之后-要做的事''' return ret return inner @wrapper def holiday(day): print('全体放假%s天'%day) return '好开心' ret = holiday(3) print(ret) print(holiday.__name__) # 测试 不使用@wraps()语法糖
使用示例
#编写装饰器,为多个函数加上认证的功能(用户名和密码来自文件) #要求登录成功一次,后学的函数无需再输入用户名和密码 def userpw(): # 定义函数打开文件 with open('up', encoding='utf-8') as f: l = [] for i in f: l.append(i.strip()) return l t = False def login(func): # 装饰器 def inner(*args, **kwargs): l = userpw() # 调用函数打开文件 global t # 申明全局变量 if t: # 等同于t = True ret = func(*args, **kwargs) # 被装饰的函数 return ret else: username = input('username:') if username == l[0]: password = input('password:') if password == l[1]: t = True # 改变全局变量 ret = func(*args, **kwargs) # 被装饰的函数 return ret else: print("password error") else: print("username error") return inner @login def test(): print('hello') @login def test1(): print('hello1') test() test1()
带参数的装饰器
技巧:先写一个装饰器,然后在装饰外层再嵌套一个函数,返回装饰器的函数名,然后再通过语法糖调用该函数即可:@外层函数(形参)
#假如你有300个参数,计算运行时间,你需要统一控制该装饰器是否生效
#初始版本
import time
def timmer(func):
def inner(*args,**kwargs):
start = time.time()
ret = func(*args,**kwargs)
end = time.time()
print(end - start)
return ret
return inner
@timmer
def test():
time.sleep(0.1)
@timmer
def test1():
time.sleep(0.1)
test()
test1()
#进阶版本,带参数
import time
FLAG = True # 定义全局变量
def timmer_out(flag):
def timmer(func):
def inner(*args,**kwargs):
if flag: # 判断全局变量是否为True
start = time.time()
ret = func(*args,**kwargs)
end = time.time()
print(end - start)
return ret
else:
ret = func(*args,**kwargs)
return ret
return inner
return timmer
#@timmer_out(FLAG) 等用于下面两句的组合
#timmer = timmer_out(FLAG)
#@timmer
@timmer_out(FLAG) # 因为加了括号 所以先调用timmer_out()函数,该函数运行后返回timmer,即@timmer了
def test():
time.sleep(0.1)
@timmer_out(FLAG)
def test1():
time.sleep(0.1)
test()
test1()
FLAG=True # 改变全局参数
test()
test1()
多个装饰器装饰一个函数
注意:语法糖的运行顺序,可以通过Debug模式进行观察
技巧:类似俄罗斯套娃,先执行第一个语法糖的第一段文字,接着执行第二个语法糖的第一段文字,接着执行被装饰的函数,再接着执行第二个语法糖的最后一句,最后执行第一个语法糖的最后一句

ef wrapper1(func): # func --> f def inner1(): print('这是第一个函数的前一句') ret = func() # f() print('这是第一个函数的后一句') return ret return inner1 def wrapper2(func): # func --> inner1 def inner2(): print('这是第二个函数的前一句') ret = func() # inner1() print('这是第二个函数的后一句') return ret return inner2 # # # 根据语法糖的规则,@wrapper2 下层没有函数,无法执行,@wrapper1下层有函数,就优先执行@wrapper1 @wrapper2 # f = wrapper2(f) =wrpper2(inner)== inner2 因为已经执行了wrapper1 f已经赋值返回inner1 @wrapper1 # f = wrapper1(f) == inner1 def f(): print('这是被装饰的函数') return "哈哈哈" ret = f() # inner2() print(ret) #常用地方 #记录用户的登陆情况 #计算这个函数的运行时间

浙公网安备 33010602011771号