装饰器(闭包函数的一种应用) 1. 什么是装饰器 器:指的是具备某一功能的工具 装饰:指的是为被装饰器对象添加新功能 装饰器就是用来为被装饰器对象添加新功能的工具 需要注意的是:装饰器本身可以是任意可调用对象,被装饰器的对象也可以是任意可调用对象 2. 为何要用装饰器 因为在实际开发环境中,是不允许对原代码进行操作,而且不能破坏原有的调用关系,根据实际要求还需要增添新功能 开发需要遵循的一个原则 开放封闭原则:封闭指的是对修改封闭,对扩展开放,就是保持原有功能并新增新的功能 装饰器的实现必须遵循两大原则: 1. 不修改被装饰对象的源代码,被操作的函数的源代码是不能进行修改 2. 不修改被装饰器对象的调用方式 装饰器的目标:就是在遵循1和2原则的前提下为被装饰对象添加上新功能 3. 怎么用装饰器 无参装饰器 装饰器不需要参数传入 有参装饰器 装饰器需要参数传入 # 装饰器模板 def outter(func): def wrapper(*args,**kwargs): #在调用函数前加功能 res=func(*args,**kwargs) #调用被装饰的\也就是最原始的那个函数 #在调用函数后加功能 return res return wrapper 注意: 1.需要注意的是装饰器最多有三层 2.解释装饰器时@语法的时候是”自下而上运行“ 3.执行装饰器时装饰器内的那个wrapper函数时的是”自上而下“ 语法糖 什么是语法糖? 使用了”@”语法糖后,就不需要额外代码来给”被装饰函数”重新赋值了, 其实”@装饰器函数”的本质就是”被装饰函数 = 装饰器函数(被装饰函数#原始的被装饰函数的内存地址)”。 为什么用语法糖? 使用语法糖让代码简化,不需要重复在调用装饰器时,进行重复的赋值操作。
一、 无参装饰器及其升级版
1 开放封闭原则
软件一旦上线后,就应该遵循开放封闭原则,即对修改源代码是封闭的,对功能的扩展是开放的。也就是说我们必须找到一种解决方案:能够在不修改一个功能源代码以及调用方式的前提下,为其加上新功能。
总结原则如下:
1、不修改源代码
2、不修改调用方式
目的:在遵循1和2原则的基础上扩展新功能
2、什么是装饰器?
器指的工具,装饰指的是为被装饰对象添加新功能。装饰器即在不修改被装饰对象源代码与调用方式的前提下,为被装饰器对象添加新功能。
装饰器与被装饰的对象均可以是任意可调用的对象
装饰器=》函数
被装饰的对象=》函数
3、给函数加上了功能,但改变了原代码
import time
def index():
start_time=time.time() #time.time()调的是1970年至现在一共过了多少秒
time.sleep(3)
print('welcome to index page')
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))
index()
4. 没有把常用功能抽象成函数,需要反复输入
import time
def index():
time.sleep(3)
print('welcome to index page')
def home(name):
time.sleep(5)
print('welcome %s to home page' %name)
start_time=time.time()
index()
stop_time = time.time()
print('run time is %s' % (stop_time - start_time))
start_time=time.time()
home('egon')
stop_time = time.time()
print('run time is %s' % (stop_time - start_time))
5、修改了原函数的调用方式,即无法用index()的形式直接调用
import time
def index():
time.sleep(3)
print('welcome to index page')
def home(name):
time.sleep(5)
print('welcome %s to home page' %name)
def wrapper(func): #func=index
start_time=time.time()
func() #index()
stop_time = time.time()
print('run time is %s' % (stop_time - start_time))
wrapper(index) # 修改了原函数的调用方式
6、 初步解决了问题,但没有考虑到原参数带返回值,或带参数的情况
import time
def index():
time.sleep(3)
print('welcome to index page')
def outter(func): #func=最原始的index
# func=最原始的index
def wrapper():
start_time=time.time()
func()
stop_time=time.time()
print(stop_time-start_time)
return wrapper
index=outter(index) # 新的index=wrapper
index() #wrapper()
7、 考虑了原参数带返回值或参数的问题,但每次都要加上index=timmer(index)
import time
def index():
time.sleep(1)
print('welcome to index page')
return 122
def home(name):
time.sleep(2)
print('welcome %s to home page' %name)
#==============装饰器
def timmer(func):
#func=最原始的home
def wrapper(*args,**kwargs):
start_time=time.time()
res=func(*args,**kwargs) #调用最原始的home
stop_time=time.time()
print(stop_time-start_time)
return res
return wrapper
index=timmer(index) # 新的index=wrapper
home=timmer(home) #新的home=wrapper
# ==========================================
index()
home('egon')
无参装饰器模板
def outer(func):
def inner(*args,**kwargs):
res=func(*args,**kwargs)
return res
return inner
8、运用装饰器语法糖 @timmer #index=timmer(index)
@timmer必须放在原函数的正上方,
timmer的函数体要放在@timmer之前
如果要用装饰器加@。。。,如果不用注释掉@
import time
def timmer(func):
def wrapper(*args,**kwargs):
start_time=time.time()
res=func(*args,**kwargs)
stop_time=time.time()
print(stop_time-start_time)
return res
return wrapper
@timmer #index=timmer(index)
def index():
time.sleep(1)
print('welcome to index page')
return 122
@timmer # home=timmer(home)
def home(name):
time.sleep(2)
print('welcome %s to home page' %name)
index()
home('egon')
9、认证装饰器实现(另一个例子)
import time
current_user={
'username':None,
# 'login_time':None
}
def auth(func):
# func=index
def wrapper(*args,**kwargs):
if current_user['username']:
print('已经登陆过了')
res=func(*args,**kwargs)
return res
uname=input('用户名>>: ').strip()
pwd=input('密码>>: ').strip()
if uname == 'egon' and pwd == '123':
print('登陆成功')
current_user['username']=uname
res=func(*args,**kwargs)
return res
else:
print('用户名或密码错误')
return wrapper
@auth #index=auth(index)
def index():
time.sleep(1)
print('welcome to index page')
return 122
@auth
def home(name):
time.sleep(2)
print('welcome %s to home page' %name)
index()
home('egon')
10、叠加多个装饰器,上面的装饰器作用为:下方装饰器+原函数
import time
current_user={
'username':None,
# 'login_time':None
}
def auth(func):
# func=index
def wrapper(*args,**kwargs):
if current_user['username']:
print('已经登陆过了')
res=func(*args,**kwargs)
return res
uname=input('用户名>>: ').strip()
pwd=input('密码>>: ').strip()
if uname == 'egon' and pwd == '123':
print('登陆成功')
current_user['username']=uname
res=func(*args,**kwargs)
return res
else:
print('用户名或密码错误')
return wrapper
def timmer(func):
def wrapper(*args,**kwargs):
start_time=time.time()
res=func(*args,**kwargs)
stop_time=time.time()
print(stop_time-start_time)
return res
return wrapper
@timmer # timmer 统计的是auth+index的执行时间
@auth
def index():
time.sleep(1)
print('welcome to index page')
return 122
index()
二、 有参数的装饰器 #再包上一层,函数的参数可以为一个,也可为多个
import time
current_user={
'username':None,
# 'login_time':None
}
def auth(engine):
# engine='file'
def auth2(func):
# func=index
def wrapper(*args,**kwargs):
if engine == 'file':
if current_user['username']:
print('已经登陆过了')
res=func(*args,**kwargs)
return res
uname=input('用户名>>: ').strip()
pwd=input('密码>>: ').strip()
if uname == 'egon' and pwd == '123':
print('登陆成功')
current_user['username']=uname
res=func(*args,**kwargs)
return res
else:
print('用户名或密码错误')
elif engine == 'mysql':
print('基于MyQL的认证')
elif engine == 'ldap':
print('基于LDAP的认证')
return wrapper
return auth2
@auth('ldap') #@auth2 #index=auth2(index) #index=wrapper
def index():
time.sleep(1)
print('welcome to index page')
return 122
index() # wrapper()
装饰器补充functools功能模块
因为Python装饰器(decorator)在实现的时候,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性
会发生改变),为了不影响,Python的functools包中提供了一个叫wraps的decorator来消除这样的副作用。写
一个decorator的时候,最好在实现之前加上functools的wrap,它能保留原有函数的名称和docstring。
具体实现形式,是导入from functiontools import wraps,在函数内调用时就能实现属性一直性。
实例:
不加wraps情况
from functools import wraps
def my_decorator(func):
# @wraps(func)
def wrapper(*args, **kwargs):
'''decorator'''
print('Calling decorated function...')
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""Docstring"""
print('Called example function')
print(example.__name__, example.__doc__)
输出结果:
wrapper decorator
加wraps情况
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
'''decorator'''
print('Calling decorated function...')
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""Docstring"""
print('Called example function')
print(example.__name__, example.__doc__)
输出结果:
example Docstring
内容总结:
装饰器(闭包函数的一种应用)
无参装饰器
有参装饰器