python 装饰器
一、核心思想
在不改变被装饰对象内部代码和原有调用方式的基础之上在添加额外的功能
二、装饰器的实现过程
根据实际需要,一步一步满足需求,完成对装饰器的理解
1、简易版本
给index函数添加统计执行时间的功能
import time def index(): time.sleep(3) print('from index') def home(): time.sleep(2) print('from home') def func(): time.sleep(2) print('from home') def outer(func): # 外层函数 def get_time(): # 内层函数 # 1. 在函数执行之前打一个时间点 start_time = time.time() func() # index() # 2. 在函数执行完毕之后在打一个时间点 end_time = time.time() # 3. 两个时间的差值就是函数的实际执行时间 print("函数执行了:%s秒" % (end_time - start_time)) return get_time # 返回内层函数 # 利用闭包的形式来传参 # res = outer(index) # res:get_time的内存地址 # res() # get_time() home = outer(home) home()
2、
当执行不同的函数,计算不同函数的执行时间,有的函数有参数,有的函数没参数
def index(name, username): time.sleep(3) print('from index') def home(name): time.sleep(2) print('from home', name) def outer(func): def get_time(*args, **kwargs): # 1. 在函数执行之前打一个时间点 start_time = time.time() func(*args, **kwargs) # 原有函数 # 2. 在函数执行完毕之后在打一个时间点 end_time = time.time() # 3. 两个时间的差值就是函数的实际执行时间 print("函数执行了:%s秒" % (end_time - start_time)) return get_time index = outer(index) index('tony', username='tank')
3、
def home(name): time.sleep(2) print('from home', name) def index(): time.sleep(3) print('from index') return 'from index' def outer(func): # func = index def get_time(*args, **kwargs): # func:index # 1. 在函数执行之前打一个时间点 start_time = time.time() res=func(*args, **kwargs) # index() func('tony') # 2. 在函数执行完毕之后在打一个时间点 end_time = time.time() # 3. 两个时间的差值就是函数的实际执行时间 print("函数执行了:%s秒" % (end_time - start_time)) return res return get_time index = outer(index) res=index() print(res)
4、
def outer(func): def inner(*args, **kwargs): print('这个是函数执行之前可以添加的功能') res = func(*args, **kwargs) print('这个是函数执行之后可以添加的功能') return res return inner def index(): print('from index') index = outer(index) index()
5、
''' 只要有一个函数认证成功,其他函数就不用验证了 需要一个变量来保存登录用户登录成功的标志 ''' is_login = {'is_login': False} # 使用闭包函数实现装饰器 def login(func): def auth(*args, **kwargs): # 可变长参数:接受多余的位置参数和关键字参数,没有参数则接受为空,不报错,正常执行,满足有参和无参两种使用场景 if is_login.get('is_login'): # 字典 get 方法取值为 False,表示 if False:条件不成立时不执行 # 前一个函数执行完,is_login赋值为 True,执行下面的代码 res = func(*args, **kwargs) # 调用函数,函数的返回值赋值给 res,再返回 res 给到 auth 函数 return res # 简易登录认证系统 username = input("Enter username: ").strip() passwd = input("Enter password: ").strip() # 验证用户名密码 if username == 'jingzhiz' and passwd == '123': print('登录成功!') # 函数执行前的操作 res = func(*args, **kwargs) # 要执行的函数 is_login['is_login'] = True return res # 函数执行后的操作 else: print('认证失败!') return auth ''' 语法糖需要定义在装饰器的下方,函数的上方紧贴着 去掉语法糖可以实现原函数的正常调用 语法糖格式@+装饰器外层函数名 @login的理解 index=login(),index 只是一个变量名,也可以是res,只是为了满足装饰器思想:不改变原函数的调用方式写成原函数名,打印内存地址于原函数不是一个 login()会返回 auth 的(函数名)即内存地址,实现 index = auth 即 index()=auth()。 ''' @login def index(name): print('from index', name) return '返回值测试' @login def home(): print('from home') # 装饰器的最终效果:保持函数的原始调用方式的同时,增加新的功能 res = index('jinghiz') print(res) home()
6、装饰器修复技术(了解)
functools.wraps函数可以拷贝原函数的属性信息。warps源码中通过使用partial函数复制了原函数的信息。
from functools import wraps import time def outer(func): # func = index @wraps(func) def get_time(*args, **kwargs): # func:index # 1. 在函数执行之前打一个时间点 start_time = time.time() res = func(*args, **kwargs) # index() func('tony') # 2. 在函数执行完毕之后在打一个时间点 end_time = time.time() # 3. 两个时间的差值就是函数的实际执行时间 print("函数执行了:%s秒" % (end_time - start_time)) return res return get_time @outer def index(): time.sleep(2) print('from index') print(index) index() print(index)
7、双层语法糖
使用场景:将登录验证、计算函数执行时间两个功能附加给一个原函数
# 双层语法糖 import time # 登录验证功能 def login_auth(func): # func = index def auth(*args, **kwargs): # 写登录功能 # 1. username = input('username:').strip() password = input('password:').strip() # 2. 验证用户名和密码是否正确 if username == 'kevin' and password == '123': print('登录成功') res = func(*args, **kwargs) return res else: print('用户名或者密码错误') return auth # 计算函数执行时间 def get_time(func): # func = index def inner(*args, **kwargs): # func:index # 1. 在函数执行之前打一个时间点 start_time = time.time() res = func(*args, **kwargs) # index() func('tony') # 2. 在函数执行完毕之后在打一个时间点 end_time = time.time() # 3. 两个时间的差值就是函数的实际执行时间 print("函数执行了:%s秒" % (end_time - start_time)) return res return inner @login_auth # index=login_auth(get_time内部的inner函数名) @get_time # get_time内部的inner函数名,等价于get_time(index) def index(): time.sleep(2) print('from index') index() # auth()
从下往上执行,最外层语法糖使用下面的函数名,即index = login_auth(inner)

- 首先,装饰器
@get_time被执行,它将函数index作为参数传递给装饰器函数get_time。因为get_time是一个装饰器函数,它会返回一个新的函数inner来替换原来的函数index。 - 接着,装饰器
@login_auth被执行,它将函数inner作为参数传递给装饰器函数login_auth。因为login_auth也是一个装饰器函数,它会返回一个新的函数auth来替换原来的函数inner。 - 现在,函数
index已经被装饰成auth函数,即index = login_auth(get_time(index))。 - 当我们调用函数
index()时,实际上是调用被装饰后的auth函数。auth函数首先执行登录验证,验证通过后再执行原始函数inner。 - 函数
inner中,先记录下函数执行之前的时间,然后再执行原始函数index(即被装饰的函数)。在执行完原始函数后,再记录下函数执行之后的时间,并计算出函数实际执行的时间。 - 最后,函数
auth返回原始函数inner的返回值,即函数index的返回值。
index 实际上被双重装饰了,首先是被装饰成计算函数执行时间的函数 inner,然后再被装饰成登录验证的函数 auth。这种双重装饰的方式可以让多个装饰器按照特定顺序依次作用于同一个函数上,从而实现更加复杂的功能。
8、
@outter就行。有参数@outter('实参')
# 有参装饰器就是带参数的装饰器 # 先前我们学习的装饰器就是无参装饰器:不到参数的装饰器 def outter(type): def login_auth(func): def auth(*args, **kwargs): # 写登录功能 # 1.接收用户输入账号密码 username = input('username:').strip() password = input('password:').strip() # 2. 验证用户名和密码是否正确 # with open('') """ 根据不同的参数用户名和密码来自于不同的位置 """ if type == 'file': print('用户名和密码来自于文件') elif type == 'mysql': print('用户名和密码来自于MySQL') elif type == 'oracle': print('用户名和密码来自于Oracle') # if username == 'kevin' and password == '123': # print('登录成功') # res = func(*args, **kwargs) # return res # else: # print('用户名或者密码错误') return auth return login_auth # 语法糖处添加带上参数,无参装饰器这里不用带() @outter('file') # @login_auth # 如果是index函数认证,用户名和密码来自于文件 login_auth(index, 1) def index(): print('from index') @outter('mysql') # 如果是home函数认证,用户名和密码来MySQL def home(): print('from home') @outter('oracle') # 如果是func函数认证,用户名和密码来oracle def func(): print('from func') # 调用函数,原有的调用方式不变 index() home() func()
8、