装饰器的概念应用场景
概念:装饰器本质上就是一个函数, 它可以让其他函数在不需要做任何代码变动的前提下, 增加额外的功能, 装饰器的返回值也是一个函数对象.
应用场景: 比如插入日志, 性能测试, 事物处理, 缓存等等场景.
装饰器的形成过程
现在我有一个需求: 需要测试一个函数的执行时间, 在不改变这个函数代码的情况下:
import time def timmer(fn): def inner(): s_time = time.time() fn() e_time = time.time() print('函数运行时间为:', e_time-s_time) return inner def func(): time.sleep(1) print('10000行代码运行结束') func = timmer(func) func()
但是如果有多个函数, 我都想测试他们的执行时间, 那岂不是每次都得func = timmer(func)? 这样感觉还是有点麻烦, 因为这些函数的函数名都不一样.
所以python给我们提供了更简单的方法, 那就是语法糖.
import time def timmer(fn): def inner(): s_time = time.time() fn() e_time = time.time() print('函数运行时间为:', e_time-s_time) return inner @timmer #相当于 func = timmer(func) def func(): time.sleep(1) print('10000行代码运行结束') func()
上面的装饰器装饰的都是不带参数的函数, 那如果要装饰一个带参数的函数该怎么办?
import time def timmer(fn): def inner(*args, **kwargs): s_time = time.time() ret = fn(*args, **kwargs) e_time = time.time() print('函数运行时间为:', e_time-s_time) return ret return inner @timmer #相当于 func1 = timmer(func1) def func1(a,b): time.sleep(1) print('10000行代码运行结束') return a+b @timmer #相当于 func2 = timmer(func2) def func2(x,y): time.sleep(1) print('10000行代码运行结束') return x*y print(func1(2,3)) print(func2(4,5))
上面的装饰器已经非常完美了, 但是有我们正常情况下查看函数信息的方法在此都会失效:
@timmer def func1(a,b): """我是func1""" time.sleep(1) print('10000行代码运行结束') return a+b print(func1.__doc__) print(func1.__name__) # 结果: # None # inner
解决方案:
from functools import wraps import time def timmer(fn): @wraps(fn) def inner(*args, **kwargs): s_time = time.time() ret = fn(*args, **kwargs) e_time = time.time() print('函数运行时间为:', e_time-s_time) return ret return inner @timmer def func1(a,b): """我是func1""" time.sleep(1) print('10000行代码运行结束') return a+b print(func1.__doc__) print(func1.__name__) # 结果: # 我是func1 # # func1
开放封闭原则
1.对扩展是开放的
为什么要对扩展开放呢?
我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。
2.对修改是封闭的
为什么要对修改封闭呢?
因为我们写的一个函数, 很有可能已经交付给其他人使用了, 如果我们这个时候对其进行修改, 很有可能影响其他已经在使用该函数的用户.
装饰器完美的遵循了这个开放封闭原则
装饰器的主要功能和固定架构
def wrapper(fn): def inner(*args, **kwargs): """执行函数前要做的""" ret=fn(*args, **kwargs) """执行函数后要做的""" return ret return inner
from functools import wraps def wrapper(fn): @wraps(fn) def inner(*args, **kwargs): """执行函数前要做的""" ret=fn(*args, **kwargs) """执行函数后要做的""" return ret return inner
带参数的装饰器
假如你有成千上万个函数使用了一个装饰器, 现在你又想把这些装饰器全部取消掉, 那该怎么办?
总不可能一个一个去删掉吧, 万一过两天你又想加上去呢?
import time def outer(flag): def timmer(fn): def inner(*args, **kwargs): if flag: print("执行函数前要做的") ret = fn(*args, **kwargs) if flag: print("执行函数后要做的") return ret return inner return timmer flag = 0 @outer(flag) def func1(a,b): """我是func1""" time.sleep(1) print('10000行代码运行结束') return a+b print(func1(2,3))
多个装饰器装饰一个函数
def wrapper1(func): # func == f 函数名 def inner1(): print('wrapper1 ,before func') # 2 func() print('wrapper1 ,after func') # 4 return inner1 def wrapper2(func): # func == inner1 def inner2(): print('wrapper2 ,before func') # 1 func() 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() # 结果: # wrapper2 ,before func # wrapper1 ,before func # in f # wrapper1 ,after func # wrapper2 ,after func
装饰器的简单应用举例
需求:在第一次进入博客园的页面之前需要先登录账户.
dic_status = { 'username': None, 'status': False, } def login(fn): def inner(*args, **kwargs): if dic_status['status'] == True: fn(*args, **kwargs) return else: username=input('用户名:') password=input('密码:') with open('userinfo',mode='r',encoding='utf-8') as f: for line in f: lis = line.strip().split("|") if username == lis[0] and password == lis[1]: dic_status['username']=username dic_status['status']=True break else: print("登录失败") inner(*args, **kwargs) return inner @login def article(): print('欢迎登录文章页面') @login def diary(): print('欢迎登录日记页面') @login def comment(): print('欢迎登录评论页面') article() comment()