一、装饰器的形成
1、装饰器的形成(装饰不带参数的函数)
需求1: 用一段代码测试一下函数的执行效率
import time print(time.time()) # 输出当前时间 def func1(): time.sleep(1) # 等待1秒 print(111) def func2(): time.sleep(1) print(222) start_time = time.time() # 开始时间 func1() # 执行此函数 end_time = time.time() # 结束时间 print('此函数的执行时间是%s' % (end_time-start_time)) # 输出执行时间 =结束时间-开始时间 start_time = time.time() func2() end_time = time.time() print('此函数的执行时间是%s' % (end_time-start_time))
但是如果有多个函数,都想测试他们的执行时间,是不是每次都得func1 = timer(func1)?
这样还是有点麻烦,因为这些函数的函数名可能是不相同,有func1,func2,graph,等等
第一版本不好,重复造轮子,测试函数的效率 必须得复制代码测试。
改版:使用函数
# 将代码封装到函数中,基本完成 import time def func1(): time.sleep(1) print(111) def func2(): time.sleep(1) print(222) def timmer(f): start_time = time.time() f() end_time = time.time() print('此函数的执行时间是%s' % (end_time-start_time)) timmer(func1) timmer(func2)
需求2: 生产环境测试函数的真正的执行效率:线上500个函数
线上问题:
线上的函数你不改变
函数的原本的调用方式不能改变
原本线上的调用方式:
func1
func2
但是用了测试效率的函数之后
def timmer(f):
start_time = time.time()
f()
end_time = time.time()
print('此函数的执行时间是%s' % (end_time-start_time))
此时已经改变了函数的调用方式:
func1()
timmer(func1)
没有改变函数里面的函数体,但是改变了函数的调用方法,所以要解决:用最少的代码,解决调用方式一致性的问题。
最终完成的需求 func1() 这个指令既要执行func1函数,又要测试效率
# 利用闭包,在不改变函数体的情况下 import time def func1(): time.sleep(1) print(111) def func2(): time.sleep(1) print(222) def timmer(f): # f = func1 函数的内存地址 def inner(): start_time = time.time() f() end_time = time.time() print('此函数的执行时间是%s' % (end_time-start_time)) return inner func1 = timmer(func1) # = inner func1() # inner() def func1(): print(func1) func1 = 777 print(func1)
功能实现了,但是有点复杂,还可以继续优化
import time def timmer(f): # f = func1 函数的内存地址 def inner(): start_time = time.time() f() end_time = time.time() print('此函数的执行时间是%s' % (end_time-start_time)) return inner def func1(): time.sleep(1) print(111) def func2(): time.sleep(1) print(222) def func3(): time.sleep(1) print(333) func1 = timmer(func1) func1() # inner() func2 = timmer(func2) func2() func3 = timmer(func3) func3()
上面还是不够优化,python 提供了更简洁的方法--语法糖
import time def timmer(f): # f = func1 函数的内存地址 def inner(): start_time = time.time() f() end_time = time.time() print('此函数的执行时间是%s' % (end_time - start_time)) return inner @timmer # func1 = timmer(func1) def func1(): time.sleep(1) print(111) func1() # inner() @timmer def func2(): time.sleep(1) print(222) func2() def func3(): # 这个不会执行,没有加语法糖 time.sleep(1) print(333)
2、装饰带参数的函数
import time def timmer(f): # f = func1 函数的内存地址 def inner(*args, **kwargs): start_time = time.time() f(*args, **kwargs) end_time = time.time() print('此函数的执行时间是%s' % (end_time - start_time)) return inner @timmer # func1 = timmer(func1) def func1(a): time.sleep(0.6) print('来了,%s' % (a)) func1('大锤')
3、装饰有返回值的函数
import time def timmer(f): # f = func1 函数的内存地址 def inner(*args, **kwargs): start_time = time.time() ret = f(*args, **kwargs) end_time = time.time() print('此函数的执行时间是%s' % (end_time - start_time)) return ret return inner @timmer # func1 = timmer(func1) def func1(a): time.sleep(1) print('来了,%s' % (a)) return 666 print(func1('大锤')) # print(inner('alex'))
小总结:
装饰器本质其实闭包,
装饰器在不改变原函数的内部函数体以及调用方式的前提下,给函数增加了额外的功能:登录,注册,打印日志,函数效率等等。
4、装饰器的基本结构
def wrapper(f): def inner(*args, **kwargs): ''' 执行被装饰的函数之前的操作''' print('执行之前', 555) ret = f(*args, **kwargs) print('执行之后', 666) ''' 执行被装饰的函数之后的操作''' return ret return inner @wrapper def func(): pass func()
二、装饰器的应用
1、装饰器的固定格式
# 装饰器的固定格式: def wrapper(f): def inner(*args, **kwargs): ret = f(*args, **kwargs) return ret return inner
2、装饰器的应用
# 登录认证 login_status = { 'username': None, 'status': False, } def login(f): def inner(*args, **kwargs): if login_status['status']: ret = f(*args, **kwargs) return ret else: print('请先登录') username = input('请输入用户名').strip() password = input('请输入密码').strip() if username == '大锤' and password == '123': login_status['username'] = username login_status['status'] = True ret = f(*args, **kwargs) return ret return inner @login def article(): print('欢迎访问文章页面') @login def dariy(): print('欢迎访问日记页面') @login def comment(): print('欢迎访问评论页面') dic = { 1: login, 2: article, 3: dariy, 4: comment, } while 1: print(''' 欢迎来到博客园首页: 1,登录 2,文章页面 3,日记页面 4,评论页面 ''') num = input('请输入数字:').strip() dic[int(num)]()
三、带参数的装饰器
需求: 500个函数加上装饰器,再去掉,在加 ,再去....
# 基本格式 def login(a, b): print(a, b) def wrapper(f): def inner(*args, **kwargs): ret = f(*args, **kwargs) return ret return inner return wrapper @login(1, 2) def func(): print(111) func()
举例
# 带参数的装饰器 # 登录认证 login_status = { 'username': None, 'status': False, } def login(a): def wrapper(f): def inner(*args, **kwargs): if a: if login_status['status']: ret = f(*args, **kwargs) return ret else: print('请先登录') username = input('请输入用户名').strip() password = input('请输入密码').strip() if username == '大锤' and password == '123': login_status['username'] = username login_status['status'] = True ret = f(*args, **kwargs) return ret else: ret = f(*args, **kwargs) return ret return inner return wrapper flag = False @login(flag) def func1(): print(111) @login(flag) def func2(): print(12) @login(flag) def func3(): print(131) func1()
login_status = { 'username': None, 'status': False, } def login(a): def wrapper(f): def inner(*args, **kwargs): if login_status['status']: ret = f(*args, **kwargs) return ret else: print('请先登录') '''根据a 京东,天猫 去验证不同密码''' username = input('请输入用户名').strip() password = input('请输入密码').strip() if username == '二狗' and password == '123': login_status['username'] = username login_status['status'] = True ret = f(*args, **kwargs) return ret return inner return wrapper @login('京东') def jd(): print('欢迎访问文章页面') @login('京东') def jdmarkte(): print('欢迎访问日记页面') @login('天猫') def TM(): print('欢迎访问评论页面') @login('天猫') def TMmarke(): print('欢迎访问评论页面')
进阶:多个装饰器装饰一个函数,注意顺序
# 多个装饰器装饰一个函数 def wrapper1(func): # func = 函数名f def inner1(): print('wrapper1 ,before func') # 2 func() # 函数f print('wrapper1 ,after func') # 4 return inner1 def wrapper2(func): # func = inner1 def inner2(): print('wrapper2 ,before func') # 1 func() # inner1() print('wrapper2 ,after func') # 5 return inner2 @wrapper2 # f = wrapper2(f) 里面的f 是inner1 外面的f 是inner2 @wrapper1 # f = wrapper1(f) 里面的f是函数名f 外面的f是 inner1 def f(): print('in f') # 3 f() # inner2()