装饰器
在不改变被装饰对象"内部代码"以及"调用方式"的基础上添加新的功能
开放封闭原则
对扩展开放
对修改封闭
装饰器原则:让使用者察觉不到程序被增加
尝试添加装饰器
1.原来的程序
def old(): # 建立函数
print('假装这是代码!') # 函数体代码
return 123 # 返回值
old()
假装这是代码!
2.新加入的功能
# 添加新的功能,不能更改原来的函数体代码和调用方式
print('新的代码!') # 1.直接添加新的功能
old() # 2.运行函数体代码
print('又是一个新的代码!') # 3.添加另一个功能
新的代码!
假装这是代码!
又是一个新的代码!
#可以运行,但如果需要多个位置需要添加同样的功能就会繁琐
#解决需要重复的问题
3.解决代码需要重复的问题
def new(): # 将上一个代码封装成函数,可以在任意位置调用
print('新的代码!')
old()
print('又是一个新的代码!')
new()
新的代码!
假装这是代码!
又是一个新的代码!
# 问题,只可以运行相同的代码,没有实际意义,需要灵活运用
#想办法灵活的将新代码添加到其他代码之上
def old():
print('假装这是代码!')
return 123
def old_1(): # 1。另一个需要同样功能的代码
print('另一个需要新增功能的代码!')
def new(a): # 2.函数调整为位置形参
print('新的代码!')
a() # 4.传入的实参直接代替a,也就是函数加上括号就会运行
print('又是一个新的代码!')
#可以运行,但如果需要多个位置需要添加同样的功能就会繁琐
# 函数封装可以在任意位置使用
new(old) # 3.调用new并将old以实参输入,
new(old_1) # 3.1调用new并将old_1以实参输入,
新的代码!
假装这是代码!
又是一个新的代码!
新的代码!
另一个需要新增功能的代码!
又是一个新的代码!
#解决了代码不能灵活运用的问题,但更改了原来的传参方式
#需要解决传参的问题
def old():
print('假装这是代码!')
return 123
def old_1():
print('另一个需要新增功能的代码!')
def new_1(): # 1.创建一个闭包函数
# 但是将这个写死了,没办法改变 a=old,
a = old # 2.将old直接赋值给下面的a。然后运行
def new(): # 3.去掉new需要的形参
print('新的代码!')
a() # 4.a被old赋值,运行到这里会自动找到old
print('又是一个新的代码!')
return new # 5.new_1返回的值为new
res = new_1() # 6.将返回来的值赋值给res
res() # 7.返回来的new加上括号就会直接运行函数体代码
# 更改了传参方式,添加闭包函数
def old():
print('假装这是代码!')
return 123
def old_1():
print('另一个需要新增功能的代码!')
def new_1(a): # 1.为了防止代码不被写死,将new_1调整为需要个实参才可以,填入的这个参数就是需要运行的old或者old_1都可以
def new():
print('新的代码!')
a() # 2.这里的a就是传入的参数
print('又是一个新的代码!')
return new
old= new_1(old) #3.关键
old()
# 没有修改原代码,没有改变传参方式
但如果给其他函数添加同样的功能需要重新填写赋值等号
old_1 = new_1(old_1)
old_1()
# 那么有没有办法可以在方便一些呢,或者可以添加有参函数呢
'''
关键:代码运行到old=new_1(old)会先运行new_1()的函数,并将old以实参的方式传递进去,此时a=old的内存地址,new_1的返回值时new赋值old。(原函数名old内存地址已经被a接收,old这个变量名现在可以随时指向新的内存地址)然后old()是运行new的函数体代码,old指向的就是new。运行new时a就会使用原old的内存地址(也就是运行old函数体代码)
'''
装饰器繁琐
def old(): # 1.函数old需要参数,调用需要参数
print('假装这是代码!')
return 123
def old_1(b):
print('另一个需要新增功能的代码!')
def new_1(a):
def new(*args,**kwargs): # 1.new需要位置参数或者关键字参数都可以运行
print('新的代码!')
a(*args,**kwargs) # 2.传入任何参数都可以使用或者不传入参数
print('又是一个新的代码!')
return new
old = new_1(old)
old()
old_1 = new_1(old_1)
old_1(1) # 3.最后需要运行的函数需要参数所以当把new返回的时候填入参数,现在old_1就是new。运行到a的时候填入的参数会自动填入
# 完全实现了可以给无参或者有参添加新功能
但没有返回值
装饰器返回值
def old():
print('假装这是代码!')
return 123
def old_1(b):
print('另一个需要新增功能的代码!')
return 456
def new_1(a):
def new(*args,**kwargs):
print('新的代码!')
l1 = a(*args,**kwargs) # 1.将原代码的返回值设置变量名,接受返回值
print('又是一个新的代码!')
return l1 #2.将返回值返回出去
return new
old = new_1(old)
l2=old() # 3.将返回值接收
old_1 = new_1(old_1)
l3=old_1(1) # 3.1将返回值接收
print(l2) #打印
print(l3) #打印
登录认证
def add():
print('这是111')
def app():
print('这是222')
def xpp(a): #形参不可以更改
def all(*args,**kwargs): #形参不可以更改
name = input('账号').strip()
pasd = input('密码').strip()
if name == 'x'and pasd == '123':
i1 = a(*args,**kwargs)
return i1
else:
print('输入错误!')
return
return all
add = xpp(add)
i2=add()
print()
一次性登录认证
def add():
print('这是111')
def app():
print('这是222')
dict_1 = {'1':False} # 类似功能字典
def xpp(a):
def all(*args,**kwargs):
if dict_1.get('1'): # 判断字典内是False或者True
i1 = a(*args, **kwargs)
return i1
name = input('账号').strip()
pasd = input('密码').strip()
if name == 'x'and pasd == '123':
i1 = a(*args,**kwargs)
dict_1['1'] = True # 登录成功将False改成True
return i1
else:
print('输入错误!')
return all
add = xpp(add)
i2=add()
print()
装饰器模板
def outer(func):
def inner(*args,**kwargs):
print('执行函数之前添加的功能')
res = func(*args,**kwargs) #执行被装饰的函数
print('执行函数之后添加的功能')
return res #被装饰的函数返回值的返回
return inner
def index (*args,**kwargss)
print('这是语法糖')
inder = outer(inder)
语法糖
def outer(func):
def inner(*args,**kwargs):
print('执行函数之前添加的功能')
res = func(*args,**kwargs) #执行被装饰的函数
print('执行函数之后添加的功能')
return res #被装饰的函数返回值的返回
return inner
def index (*args,**kwargss)
print('这是语法糖')
inder = outer(inder)
@outer # inder = outer(inder) 相等
装饰器语法糖书写规范
语法糖必须紧贴着要装饰的对象上方
装饰器语法糖内部原理
自动将下面紧贴着的被装饰对象名字当做参数传给装饰器函数调用
双层语法糖
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
@get_time
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)
def home():
"""这是一个home函数"""
print('from home')
# help(index)
# help(home)
# print(index)
# help(len)
help() #查看函数参数
from functools import wraps
def 函数名():
@wraps(函数名)
'''就这么用,别问为什么'''
练习题
def outter1(func1):
print('加载了outter1')
def wrapper1(*args, **kwargs):
print('执行了wrapper1')
res1 = func1(*args, **kwargs)
return res1
return wrapper1
def outter2(func2):
print('加载了outter2')
def wrapper2(*args, **kwargs):
print('执行了wrapper2')
res2 = func2(*args, **kwargs)
return res2
return wrapper2
def outter3(func3):
print('加载了outter3')
def wrapper3(*args, **kwargs):
print('执行了wrapper3')
res3 = func3(*args, **kwargs)
return res3
return wrapper3
@outter1
@outter2
@outter3
def index():
print('from index')
七个print的打印顺序
有参装饰器
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()
#有参函数,最外层函数在包一层