装饰器简介
装饰器前戏
装饰器并不是一个新的知识
而是由之前的名称空间,函数对象,闭包函数组合而来
装饰器
器:指的是工具
装饰:给被装饰对象添加额外的功能
装饰器的原则
开放封闭原则
开发:对扩展开放
封闭:对修改封闭
装饰器核心思想
在不改变被"装饰对象内部代码" 和 "原有调用方式" 的基础之上添加额外功能
def index():
print('from index')
index()
import time
print(time.time()) # 1637036293.0609405
# 获取的结果叫时间戳(运行代码的那一刻距离1970-1-1所经历的秒数)
time.sleep(3) # 让程序原地等待3秒
print('睡饱了')
def index():
time.sleep(3)
print('from index')
# 统计index函数的执行时间
# 在函数运行之前统计一次时间
start_time = time.time()
index()
# 在函数运行完毕之后再次统计
end_time = time.time()
# 计算差值
print(end_time - start_time)
装饰器简易版本
# 给函数添加统计执行时间的功能
def outer(func): # func指的是函数名index
# func = index
def get_time():
start_time = time.time()
func()
end_time = time.time()
print('函数运行时间:%s' % (end_time - start_time))
return get_time # 将get_time函数名返回出去
index = outer(index) # outer(index函数名)
# 左侧的变量名index指代是函数名get_time
index()
解决参数问题
import time
def index():
time.sleep(3)
print('天王盖地虎,小鸡炖蘑菇')
def login(name):
time.sleep(1)
print('%s是谁在炖' % name)
def outer(func): # func指向的是函数名login
# func = login
def get_time(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs)
end_time = time.time()
print('函数运行时间:%s' % (end_time - start_time))
return get_time # 将get_time函数名返回出去
login = outer(login)
login("qq")
index = outer(index)
index()
import time
def index():
time.sleep(3)
print('天王盖地虎,小鸡炖蘑菇')
return 'from index'
def login(name):
time.sleep(1)
print('%s是谁在炖' % name)
return 'from login'
def outer(func): # func指向的是函数名login
# func = login
def get_time(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs) # 接收被装饰函数的返回值
end_time = time.time()
print('函数运行时间:%s' % (end_time - start_time))
return res # 执行完get_time之后返回被装饰函数执行之后的返回值
return get_time # 将get_time函数名返回出去
index = outer(index)
res = index()
print(res)
login = outer(login)
res1 = login('qq')
print(res1)
# 天王盖地虎,小鸡炖蘑菇
# 函数运行时间:3.000620126724243
# from index
# qq是谁在炖
# 函数运行时间:1.0090103149414062
# from login
认证装饰器
import time
def index():
time.sleep(1)
print('百万大奖等你来拿')
def home():
time.sleep(1)
print('学学学 一天到晚就是学')
def register():
time.sleep(1)
print('注册功能')
# 给index函数添加认证功能
"""
在调用index之前需要用户输入用户名和密码
正确才可以调用
错误直接拒绝
"""
# 定义一个用于记录用户是否登录的数据
is_login = {'is_login': False}
def login_auth(func):
def auth(*args, **kwargs):
# 1.1 先判断用户是否已经登录
if is_login.get('is_login'):
# 3. 正常执行函数index
res = func(*args, **kwargs)
return res
# 1. 先获取用户的用户名和密码
username = input('请输入用户名:').strip()
password = input('请输入密码:').strip()
# 2. 校验用户名和密码是否正确
if username == 'jason' and password == '123':
# 3.正常执行函数index
res = func(*args, **kwargs)
# 4. 将记录用户登录状态的数据修改
is_login['is_login'] = True
return res
else:
print('用户名和密码错误 无法执行函数')
return auth
index = login_auth(index)
index()
home = login_auth(home)
home()
register = login_auth(register)
register()
装饰器固定模板
def outer(func):
def inner(*args, **kwargs):
print('执行函数之前可以添加的额外功能')
res = func(*args, **kwargs) # 执行被装饰的函数
print('执行函数之后可以添加的额外功能')
return res # 将被装饰函数执行之后的返回值返回
return inner
装饰器语法糖
def outer(func):
def inner(*args, **kwargs):
print('执行函数之前可以添加的额外功能')
res = func(*args, **kwargs) # 执行被装饰的函数
print('执行函数之后可以添加的额外功能')
return res # 将被装饰函数执行之后的返回值返回
return inner
@outer # index = outer(index)
def index(*args, **kwargs):
print('from index')
@outer # home = outer(home)
def home():
print('from home')
"""
装饰器语法糖书写规范
语法糖必须紧贴在被装饰对象的上方
装饰器语法糖内部原理
会自动将下面紧贴着的被装饰对象名字当作参数传给装饰器函数调用 # index = outer(index)
如果上方没有其他语法糖则直接使用与被装饰对象相同的变量名接收装饰器返回值
"""
双层语法糖
# 统计函数运行时间
import time
def get_time(func):
def inner(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs) # 执行被装饰的函数
end_time = time.time()
print('函数执行时间:%s'%(end_time-start_time))
return res # 将被装饰函数执行之后的返回值返回
return inner
# 校验用户登录装饰
def login_auth(func):
def inner(*args, **kwargs):
# 1.先获取用户的用户名和密码
username = input('username>>>:').strip()
password = input('password>>>:').strip()
# 2.校验用户名和密码是否正确
if username == 'jason' and password == '123':
res = func(*args, **kwargs) # 执行被装饰的函数
return res # 将被装饰函数执行之后的返回值返回
print('用户名或密码错误 无权限执行')
return inner
@login_auth # index = login_auth(get_time内部的inner函数)
@get_time # get_time内部的inner函数 = get_time(index)
def index():
time.sleep(1)
print('from index')
index()
装饰器修复技术
from functools import wraps
def outer(func):
@wraps(func) # 修复技术就是为了让被装饰对象更加不容易被察觉装饰了
def inner(*args, **kwargs):
print('执行函数之前可以添加的额外功能')
res = func(*args, **kwargs) # 执行被装饰的函数
print('执行函数之后可以添加的额外功能')
return res # 将被装饰函数执行之后的返回值返回
return inner
@outer # index = outer(index)
def index():
print('from index')
print(index)
help(index)
from functools import wraps
def outer(func):
@wraps(func) # 修复技术就是为了让被装饰对象更加不容易被察觉装饰了
def inner(*args, **kwargs):
print('执行函数之前可以添加的额外功能')
res = func(*args, **kwargs) # 执行被装饰的函数
print('执行函数之后可以添加的额外功能')
return res # 将被装饰函数执行之后的返回值返回
return inner
#
@outer # index = outer(index)
def index():
print('from index')
print(index)
help(index)
def home():
"""这是一个home函数"""
print('from home')
help(index)
help(home)
print(index)
help(len)
语法糖使用
@outer1 # index = outer1(outer2函数的返回值(函数名))
@outer2 # outer2函数的返回值(函数名) = outer2(outer3函数的返回值(函数名))
@outer3 # outer3函数的返回值(函数名) = outer3(真正的index)
def index():
pass
# 语法糖由下往上自动执行
练习题
# 判断七句print执行顺序
def outer1(func1):
print('加载了outer1')
def wrapper1(*args, **kwargs):
print('执行了wrapper1')
res1 = func1(*args, **kwargs)
return res1
return wrapper1
def outer2(func2):
print('加载了outer2')
def wrapper2(*args, **kwargs):
print('执行了wrapper2')
res2 = func2(*args, **kwargs)
return res2
return wrapper2
def outer3(func3):
print('加载了outer3')
def wrapper3(*args, **kwargs):
print('执行了wrapper3')
res3 = func3(*args, **kwargs)
return res3
return wrapper3
@outer1 # index = outer(wrapper2)
@outer2 # outer2(wrapper3)
@outer3 # outer3(真正的函数index)
def index():
print('from index')
index() # wrapper1()
有参装饰器
如果装饰器内部还需要额外的参数
from functools import wraps # 固定句式
def outers(others):
def outer(func):
@wraps(func) # 固定句式
def inner(*args,**kwargs):
# 执行被装饰函数(对象)之前可以执行的其他操作
others
res = func(*args,**kwargs)
# 执行被装饰函数(对象)之后可以执行的其他操作
return res
return inner
return outer
@outers(123) # 先执行outers函数 之后再执行语法糖功能
案例
def outer(source_data):
# source_data = 'file'
def login_auth(func):
def auth(*args,**kwargs):
# 2.校验用户名和密码是否正确
# 数据的校验方式可以切换多种
if source_data == 'file':
# 从文件中获取用户数据并比对
print('file文件获取')
elif source_data == 'MySQL':
# 从MySQL数据库中获取数据比对
print('MySQL数据库获取')
elif source_data == 'postgreSQL':
# 从postgreSQL数据库中获取数据对比
print('postgreSQL数据库获取')
else:
print('用户名或密码错误 无法执行函数')
return auth
return login_auth
@outer('file')
def index():
print('from index')
@outer('MySQL')
def home():
print('from home')
index()
home()
# file文件获取
# MySQL数据库获取