Python 学习笔记: 装饰器
Python 装饰器
1 装饰器程序初步印象
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import time def func(): time.sleep(0.01) print("程序运行干活。。。") def timer(f): def inner(): time1 = time.time() f() time2 = time.time() print(time2-time1) return inner func = timer(func) func()
使用@符号
import time def timer(f): def inner(): time1 = time.time() f() time2 = time.time() print(time2-time1) return inner @timer def func(): time.sleep(0.01) print("程序运行干活。。。") func()
2 装饰器的基本定式:
def wrapper(func): # 装饰器函数, func是被装饰的函数 def inner(*args, **kwargs): '''装饰器在装饰的函数运行前要执行的工作''' ret = func(*args,**kwargs) '''装饰器在装饰的函数运行后要执行的工作 ''' return ret # 返回被装饰函数的执行结果 return inner @wrapper # 语法糖, qqqing = wrapper(qqqing) def qqqing(a,b): # 被装饰的函数 print(a,b) return max(a,b) # 正常调用函数 print(qqqing(2,3))
3 , 带参数的装饰器。
应用场景举例: 设置一个变量Flag, Flag = True 时装饰器起作用, Flag = False 时装饰器不起作用。需要使用三层嵌套来定义。
FLAG = True def wrapper_out(flag): def wrapper(func): # 装饰器函数, func是被装饰的函数 def inner(*args, **kwargs): if flag: '''装饰器在装饰的函数运行前要执行的工作''' print('装饰器在函数运行 之前 做的事情。。。') ret = func(*args,**kwargs) '''装饰器在装饰的函数运行后要执行的工作 ''' print('装饰器在函数运行 之后 做的事情。。。') return ret # 返回被装饰函数的执行结果 else: '''装饰器不起作用时, 走这里''' ret = func(*args, **kwargs) return ret return inner return wrapper @wrapper_out(FLAG) # wrapper_out(FLAG)先执行一次, 返回wrapper, 形成 @wrapper语法糖再去装饰。 def qqqing(a,b): # 被装饰的函数 print(a,b) return max(a,b) # 正常调用函数 print(qqqing(2,3))
4 装饰器的修正, 为了不影响被装饰函数的一些属性(__name__, __doc__) 等, 使用 from functools import wraps
from functools import wraps FLAG = True def wrapper_out(flag): def wrapper(func): # 装饰器函数, func是被装饰的函数 @wraps(func) # 为了不改变被装饰函数的原有属性而增加的 def inner(*args, **kwargs): if flag: '''装饰器在装饰的函数运行前要执行的工作''' print('装饰器在函数运行 之前 做的事情。。。') ret = func(*args,**kwargs) '''装饰器在装饰的函数运行后要执行的工作 ''' print('装饰器在函数运行 之后 做的事情。。。') return ret # 返回被装饰函数的执行结果 else: '''装饰器不起作用时, 走这里''' ret = func(*args, **kwargs) return ret return inner return wrapper @wrapper_out(FLAG) # wrapper_out(FLAG)先执行一次, 返回wrapper, 形成 @wrapper语法糖再去装饰。 def qqqing(a,b): # 被装饰的函数 '''被装饰函数的原来的doc信息在这里''' print(a,b) return max(a,b) # 正常调用函数 print(qqqing(2,3)) #输出: 3 ### 这是函数的返回值 print(qqqing.__name__) # 输出: qqqing print(qqqing.__doc__) # 输出: 被装饰函数的原来的doc信息在这里
5 , 多个装饰器装饰同一个函数的情况。
from functools import wraps FLAG = True def wrapper_out(flag): def wrapper(func): # 装饰器函数, func是被装饰的函数 @wraps(func) # 为了不改变被装饰函数的原有属性而增加的 def inner(*args, **kwargs): if flag: '''装饰器wrapper在装饰的函数运行前要执行的工作''' print('装饰器wrapper在函数运行 之前 做的事情。。。') ret = func(*args,**kwargs) '''装饰器wrapper在装饰的函数运行后要执行的工作 ''' print('装饰器wrapper在函数运行 之后 做的事情。。。') return ret # 返回被装饰函数的执行结果 else: '''装饰器wrapper不起作用时, 走这里''' ret = func(*args, **kwargs) return ret return inner return wrapper import time def wrapper_out2(flag): def wrapper2(func): # 装饰器函数, func是被装饰的函数 @wraps(func) # 为了不改变被装饰函数的原有属性而增加的 def inner2(*args, **kwargs): if flag: start = time.time() print('装饰器wrapper2在装饰的函数运行前要执行的工作') ret = func(*args,**kwargs) '''装饰器wrapper2在装饰的函数运行后要执行的工作 ''' print('装饰器wrapper2在装饰的函数运行后要执行的工作') time.sleep(1) end = time.time() print('被装饰的函数运行时间为 %f'%(end-start)) return ret # 返回被装饰函数的执行结果 else: '''装饰器wrapper2不起作用时, 走这里''' ret = func(*args, **kwargs) return ret return inner2 return wrapper2 @wrapper_out2(FLAG) @wrapper_out(FLAG) # wrapper_out(FLAG)先执行一次, 返回wrapper, 形成 @wrapper语法糖再去装饰。 def qqqing(a,b): # 被装饰的函数 '''被装饰函数的原来的doc信息在这里''' print(a,b) return max(a,b) # 正常调用函数 print(qqqing(2,3)) #输出: 3 ### 这是函数的返回值 print(qqqing.__name__) # 输出: qqqing print(qqqing.__doc__) # 输出: 被装饰函数的原来的doc信息在这里
输出结果为:
装饰器wrapper2在装饰的函数运行前要执行的工作
装饰器wrapper在函数运行 之前 做的事情。。。
2 3
装饰器wrapper在函数运行 之后 做的事情。。。
装饰器wrapper2在装饰的函数运行后要执行的工作
被装饰的函数运行时间为 1.000576
3
qqqing
被装饰函数的原来的doc信息在这里
---------------
注意:被多个装饰器装饰的函数执行顺序。
如果存在下面的装饰顺序:
执行顺序为:
6 下面做一些练习题来加深印象
练习6.1 为正常调用的函数增加调用前验证用户名和密码功能, 如果通过验证则正常调用, 如果没有通过验证则不调用函数,并且一次通过后, 其它函数的调用不再验证, 验证的用户信息存储在文件中。
Flag = False def wrapper(func): def inner(*args, **kwargs): global Flag if not Flag: username = input('请输入用户名:') password = input('请输入密码:') with open('loginfo.txt', mode='r', encoding='utf-8') as f: for line in f: info = line.split('|') if info[0].strip() == username and info[1].strip() == password: Flag = True break if Flag: ret = func(*args, **kwargs) return ret return inner @wrapper def shop_add(): print('增加一件商品') @wrapper def shop_del(): print('删除一件商品') #正常调用 shop_add() shop_del()
loginfo.txt 的内容如下:(中间以‘ | ’ 分开, 表示用户名 和密码), 当然啦, 密码可以转为MD5加密的存储, 在此就不弄了。这里主要是为了学习装饰器。
alex|alex3721
jacky|jacky3721
练习6.2 编写装饰器, 在文件中把调用的函数名记录。
def log(func): def inner(*args, **kwargs): with open('log', mode='a', encoding='utf-8') as f: f.write(func.__name__ + '\n') # 记录调用函数的名字, \n 是为了每写一次换行 ret = func(*args, **kwargs) return ret return inner @log def shop_add(): print('增加一件商品') @log def shop_del(): print('删除一件商品') #正常调用 shop_add() shop_del() shop_add() shop_del() shop_add() shop_del()
练习6.3 从网上读取一个页面, 如果本地有缓存的页面, 就直接从本地读。
import os from urllib.request import urlopen def webcache(func): #装饰器 def inner(*args, **kwargs): if os.path.getsize('webcache'): with open('webcache', mode='rb') as f: return f.read() ret = func(*args, **kwargs) with open('webcache', mode='wb') as f: f.write(b'===========\n'+ret) #“===========\n" 是为了输出时能看明白是否从本地读的文件内容。 return ret return inner @webcache def get(url): code = urlopen(url).read() return code
#开始正常调用 print(get('http://www.baidu.com'))
7 类装饰器 (待续)