- 装饰器介绍
- 小练习:认证功能的装饰器
- 叠加多个装饰器
- 有参装饰器
- 装饰器伪装注释信息
-曾老湿, 江湖人称曾老大。
-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。
装饰器介绍
什么是装饰器 |
---|
装饰器,就是用来为被装饰器对象,添加新功能的工具
代码语言:javascript复制装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。
强调装饰器的原则:1 不修改被装饰对象的源代码 2 不修改被装饰对象的调用方式
装饰器的目标:在遵循1和2的前提下,为被装饰对象添加上新功能
为什么要用装饰器 |
---|
开放封闭原则:对修改封闭,对扩展开放
装饰器的实现,必须遵循两大原则。 1.不修改被装饰对象的源代码 2.不修改被装饰对象的调用方式
装饰器的目标,就是在遵循1和2原则的前提下为被装饰对象添加上新功能
装饰器简单版 |
---|
# 模拟网站响应
import time
def index():
print('welcome to index page')
time.sleep(3)
# 需求,给上面的index函数,加一个新功能,统计他的执行时间
import time
def index():
print('welcome to index page')
time.sleep(3)
start=time.time()
index()
stop=time.time()
print('Run Time Is: %s' %(stop - start))
# 是不是满足了两大原则?
# 1.没有改变被装饰对象的源代码
# 2.没有改变被装饰对象的调用方式
# 哦~~~~这原来就是装饰器啊...
# 装饰你妹啊~
# 想一个问题,如果我们有login函数,register函数...等1万个函数,改怎么写?
start=time.time()
index()
stop=time.time()
print('Run Time Is: %s' %(stop - start))
start=time.time()
register()
stop=time.time()
print('Run Time Is: %s' %(stop - start))
start=time.time()
login()
stop=time.time()
print('Run Time Is: %s' %(stop - start))
# 智障...全都是重复代码,可以去屎了,那么我们来优化一下代码
import time
def index():
print('welcome to index page')
time.sleep(3)
def wrapper(func):
start=time.time()
func()
stop=time.time()
print('Run Time Is: %s' %(stop - start))
wrapper(index)
# 完美了吗?并没有,因为每次调用我们还需要传参...
import time
def index():
print('welcome to index page')
time.sleep(3)
def outter(func):
def wrapper():
start=time.time()
func()
stop=time.time()
print('Run Time Is: %s' %(stop - start))
return wrapper
f=outter(index)
f()
# 变成"装饰器"写法,但是好像不满足两大原则,我们修改了函数的调用方式,下面的代码 ,搞定
import time
def index():
print('welcome to index page')
time.sleep(3)
def outter(func):
def wrapper():
start=time.time()
func()
stop=time.time()
print('Run Time Is: %s' %(stop - start))
return wrapper
index=outter(index)
index()
装饰器升级版 |
---|
import time
def home(name):
print('welcome %s to home page' %name)
time.sleep(2)
# 上面这个函数,是一个需要传参的函数,调用方法如下
home('zls')
# 需求,使用装饰器,调用
import time
def index():
print('welcome to index page')
time.sleep(3)
def home(name):
print('welcome %s to home page' %name)
time.sleep(2)
def outter(func):
def wrapper(name):
start=time.time()
func(name)
stop=time.time()
print('Run Time Is: %s' %(stop - start))
return wrapper
home=outter(home)
home('zls')
# 完美?那么我们把index在调用一下试试?
import time
def index():
print('welcome to index page')
time.sleep(3)
def home(name):
print('welcome %s to home page' %name)
time.sleep(2)
def outter(func):
def wrapper(name):
start=time.time()
func(name)
stop=time.time()
print('Run Time Is: %s' %(stop - start))
return wrapper
index=outter(index)
index()
# home=outter(home)
# # home('zls')

emm... 报错了,为啥呢?因为把为了写home函数的装饰器,把wrapper改了啊,需要一个参数
代码语言:javascript复制import time
def index():
print('welcome to index page')
time.sleep(3)
def home(name):
print('welcome %s to home page' %name)
time.sleep(2)
def outter(func):
def wrapper(*args,**kwargs):
start=time.time()
func(*args,**kwargs)
stop=time.time()
print('Run Time Is: %s' %(stop - start))
return wrapper
index=outter(index)
index()
home=outter(home)
home('zls')

那么问题来了,如果函数有返回值怎么办?
代码语言:javascript复制import time
def index():
print('welcome to index page')
time.sleep(3)
def home(name):
print('welcome %s to home page' %name)
time.sleep(2)
return 123
def outter(func):
def wrapper(*args,**kwargs):
start=time.time()
func(*args,**kwargs)
stop=time.time()
print('Run Time Is: %s' %(stop - start))
return wrapper
home=outter(home)
res=home('zls')
print(res)

改进一下纸
代码语言:javascript复制import time
def index():
print('welcome to index page')
time.sleep(3)
def home(name):
print('welcome %s to home page' %name)
time.sleep(2)
return 123
def timmer(func):
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
stop=time.time()
print('Run Time Is: %s' %(stop - start))
return res
return wrapper
index=timmer(index)
index()
home=timmer(home)
res=home('zls')
print(res)
装饰器语法糖 |
---|
import time
def timmer(func):
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
stop=time.time()
print('Run Time Is: %s' %(stop - start))
return res
return wrapper
@timmer
def index():
print('welcome to index page')
time.sleep(3)
# 语法糖,把装饰器,放到被装饰对象上面加上@
@timmer
def home(name):
print('welcome %s to home page' %name)
time.sleep(2)
return 123
index()
res=home('zls')
print(res)
装饰器模板 |
---|
def outter(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
@outter
小练习:认证功能的装饰器
代码语言:javascript复制import time
def auth(func):
def wrapper(*args,**kwargs):
inp_user=input('please input your username: ').strip()
inp_pwd=input('please input your password: ').strip()
if inp_user == 'zls' and inp_pwd == '123':
print('login successfull')
res=func(*args,**kwargs)
return res
return wrapper
@auth
def index():
print('welcome to index page')
time.sleep(3)
index()
叠加多个装饰器
代码语言:javascript复制import time
def timmer(func):
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
stop=time.time()
print('Run Time Is: %s' %(stop - start))
return res
return wrapper
def auth(func):
def wrapper(*args,**kwargs):
inp_user=input('please input your username: ').strip()
inp_pwd=input('please input your password: ').strip()
if inp_user == 'zls' and inp_pwd == '123':
print('login successfull')
res=func(*args,**kwargs)
return res
return wrapper
@auth
@timmer
def index():
print('welcome to index page')
time.sleep(3)
index()

注意:当我们有多个装饰器的时候,解释语法是自下而上运行,但是如果是装饰器的话,自上而下运行,如果我们把timmer装饰器放在auth上面,那么运算的时间,会把auth函数的时间加进去

有参装饰器
之前我们用的都是无参装饰器,现在我们要让装饰器可以传递参数
我们来修改一下认证的源,之前我们认证都是文件,企业中会把用户存储在MySQL中或者LDAP
代码语言:javascript复制import time
def timmer(func):
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
stop=time.time()
print('Run Time Is: %s' %(stop - start))
return res
return wrapper
def auth(func):
def wrapper(*args,**kwargs):
if engine == 'file':
inp_user=input('please input your username: ').strip()
inp_pwd=input('please input your password: ').strip()
if inp_user == 'zls' and inp_pwd == '123':
print('login successfull')
res=func(*args,**kwargs)
return res
else:
print('username or password error')
elif engine == 'mysql':
print('基于MySQL的认证机制')
elif engine == 'ldap':
print('基于LDAP的认证机制')
else:
print('无法识别的认证源')
return wrapper
@auth
@timmer
def index():
print('welcome to index page')
time.sleep(3)
index()
代码写完了,但是我需要知道engine是什么,然后找对应的认证方式,需要传参使用闭包
代码语言:javascript复制import time
def timmer(func):
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
stop=time.time()
print('Run Time Is: %s' %(stop - start))
return res
return wrapper
def engine_auth(engine='file'):
def auth(func):
def wrapper(*args,**kwargs):
if engine == 'file':
inp_user=input('please input your username: ').strip()
inp_pwd=input('please input your password: ').strip()
if inp_user == 'zls' and inp_pwd == '123':
print('login successfull')
res=func(*args,**kwargs)
return res
else:
print('username or password error')
elif engine == 'mysql':
print('基于MySQL的认证机制')
elif engine == 'ldap':
print('基于LDAP的认证机制')
else:
print('无法识别的认证源')
return wrapper
return auth
@engine_auth('mysql')
@timmer
def index():
print('welcome to index page')
time.sleep(3)
index()
装饰器,最多就三层,最外面层可以随便传递参数
无参装饰器模板 |
---|
def outter(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
有参装饰器模板 |
---|
def outter(x,y,z):
def outter2(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
return outter2
解决程序bug |
---|
import time
def timmer(func):
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
stop=time.time()
print('Run Time Is: %s' %(stop - start))
return res
return wrapper
def engine_auth(engine='file'):
def auth(func):
def wrapper(*args,**kwargs):
if engine == 'file':
inp_user=input('please input your username: ').strip()
inp_pwd=input('please input your password: ').strip()
if inp_user == 'zls' and inp_pwd == '123':
print('login successfull')
res=func(*args,**kwargs)
return res
else:
print('username or password error')
elif engine == 'mysql':
print('基于MySQL的认证机制')
elif engine == 'ldap':
print('基于LDAP的认证机制')
else:
print('无法识别的认证源')
return wrapper
return auth
@engine_auth('file')
@timmer
def index():
print('welcome to index page')
time.sleep(3)
index()
@engine_auth('file')
@timmer
def home(name):
print('welcome %s to home page' %name)
time.sleep(2)
return 123
res=home('zls')
print(res)
我们在两个函数前面都加上认证模块

然后这个程序就智障了,mmp,让我登录两次?你们见过哪个网站登录过一次,还需要登录第二次?
比如你再京东买东西...一开始登录了,然后当你点购物车还要登录?点结算还要登录?疯了吧
所以我们需要记录一下用户的登录状态...
代码语言:javascript复制import time
current_user={'username':None}
def timmer(func):
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
stop=time.time()
print('Run Time Is: %s' %(stop - start))
return res
return wrapper
def engine_auth(engine='file'):
def auth(func):
def wrapper(*args,**kwargs):
if current_user['username']:
print('已经登录过了,无需再次登录')
res=func(*args,**kwargs)
return res
if engine == 'file':
inp_user=input('please input your username: ').strip()
inp_pwd=input('please input your password: ').strip()
if inp_user == 'zls' and inp_pwd == '123':
print('login successfull')
current_user['username']=inp_user #记录登录状态
res=func(*args,**kwargs)
return res
else:
print('username or password error')
elif engine == 'mysql':
print('基于MySQL的认证机制')
elif engine == 'ldap':
print('基于LDAP的认证机制')
else:
print('无法识别的认证源')
return wrapper
return auth
@engine_auth('file')
@timmer
def index():
print('welcome to index page')
time.sleep(3)
index()
@engine_auth('file')
@timmer
def home(name):
print('welcome %s to home page' %name)
time.sleep(2)
return 123
res=home('zls')
print(res)

装饰器伪装注释信息
了解函数注释的作用 |
---|
import time
def demo(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
def index():
"""
index功能
"""
print('进入index页面')
time.sleep(3)
# 查看函数名
print(index.__name__)
# 查看函数注释信息
print(index.__doc__)
# 查看函数的注释信息
print(help(index))

现在我们把装饰器加上
代码语言:javascript复制import time
def demo(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
@demo
def index():
"""
index功能
"""
print('进入index页面')
time.sleep(3)
print('--- 函数名:---')
print(index.__name__)
print('--- 注释信息1 ---')
print(index.__doc__)
print('--- 注释信息2 ---')
print(help(index))

当我们给一个函数加上装饰器后,会发现,函数名变了,注释信息没了,这样的话,别人无法查看你的这个功能,用不明白,那就是垃圾代码。
那肿么办?我们把注释信息加在wrapper里面?
代码语言:javascript复制import time
def demo(func):
def wrapper(*args,**kwargs):
"""
index功能
"""
res=func(*args,**kwargs)
return res
return wrapper
@demo
def index():
"""
index功能
"""
print('进入index页面')
time.sleep(3)
print('--- 函数名:---')
print(index.__name__)
print('--- 注释信息1 ---')
print(index.__doc__)
print('--- 注释信息2 ---')
print(help(index))

这样写,那就是智障...写死了,如果我把装饰器加在别的函数上呢?还是index功能嘛?
代码语言:javascript复制import time
def demo(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
# 将wrapper函数的函数名赋值给func函数,也就是传递进来的函数(被装饰对象)
wrapper.__name__ = func.__name__
# 将wrapper函数的注释信息赋值给func函数,也就是传递进来的函数(被装饰对象)
wrapper.__doc__ = func.__doc__
return wrapper
@demo
def index():
"""
index功能
"""
print('进入index页面')
time.sleep(3)
print('--- 函数名:---')
print(index.__name__)
print('--- 注释信息1 ---')
print(index.__doc__)
print('--- 注释信息2 ---')
print(help(index))

但是...咱们可以看看一个函数下面有多少个__xxx__的方法,上面才有两个

我们需要使用python内置的一个装饰器
代码语言:javascript复制from functools import wraps
import time
def demo(func):
@wraps(func)
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
@demo
def index():
"""
index功能
"""
print('进入index页面')
time.sleep(3)
print('--- 函数名:---')
print(index.__name__)
print('--- 注释信息1 ---')
print(index.__doc__)
print('--- 注释信息2 ---')
print(help(index))