一、装饰器的基本知识
1 什么是装饰器
器=>工具
装饰=>指的是为被装饰对象添加新功能
所以装饰器就是为被装饰对象添加新功能的工具(目前为止学过的工具只有函数了,所以在这里是指用函数来实现装饰器的功能)
装饰器本身可以是任意可调用的对象=>函数
被装饰的对象也可以是任意可调用的对象=>函数
用装饰器的目的目标:写一个函数来为另外一个函数添加新功能
2 为何要用装饰器
开放封闭原则: 软件一旦上线就应该执行开方封闭原则,即对修改源代码是封闭的,对新功能的扩展是开放的,也就是说我们必须要找到一种解决方案,即不修改功能的源代码,也不修改功能的调用方式来实现新功能的拓展,为其加上新功能.
对修改封闭指的是:
1. 不能修改功能的源代码
2. 也不能修改功能的调用方式
对扩展开发指的是:
可以为原有的功能添加新的功能
装饰器就是要在不修改功能源代码以及调用方式的前提下为原功能添加额外新的功能
3 如何用装饰器
import time 调用时间模块
def index(): index在这里做原函数,对应的功能为原函数的功能(也就是说在对其加装饰器的时候要注意不
应该违背装饰器的使用条件)
print('welcome to index page') (这两行代码是index实现功能的基本代码)
time.sleep(3)
def outter(func): 现在又需求是计算index实现功能时一共运行了多长时间
# func=就是最原始那个index(原函数)的内存地址
def wrapper():
start=time.time() 开始计时(time.time是计算从1970年到现在时间一共走了多少秒)
func() 最原始那个index的内存地址()
stop=time.time() 停止计时,通过开始停止的时间来计算时间差得到index的运行时间
print('run time is %s' %(stop-start))
return wrapper
index=outter(index) 括号里的index当做outter的参数(func)是最原始那个index(原函数)的内存地址)
等号左边的index是outter的返回值即是函数wrapper的内存地址,在这里相当于是将index重新定义了,还是用index为定义变量的名字的原因是为了和源函数的调用方式保持一致,其实对应的内存地址已经被修改了
index() 在这里重新调用index,要注意这里的index对应的函数是outter的返回值wrapper函
数的内存地址
二、装饰器的修正
修正一:
在这里做的第一个修正是,如果原函数中有返回值,在装饰器中应该在何处加上返回值才能和原函数的返回值一样,
import time
def index():
print('welcome to index page')
time.sleep(3)
return 123
#==============================================================
def outter(func):
# func=最原始那个index的内存地址
def wrapper():
start=time.time()
res=func() # 最原始那个index的内存地址()
stop=time.time()
print('run time is %s' %(stop-start))
return res 在这里加上返回值,因为上边res=func()即将源函数的返回值返回给了res,所以
在wrapper中只需要直接返回res就可以了
return wrapper
index=outter(index) #index=outter(最原始那个index的内地址) #index=wrapper函数的内存地址
#==============================================================
res=index() #res=wraper()
print(res)
修正二:
需要装饰一个有参数的原函数,装饰器不需要做改动,
import time
def index():
print('welcome to index page')
time.sleep(3)
return 123
def home(name):
print('welcome %s to home page' %name)
time.sleep(1)
#=============================装饰器===================================
def outter(func):
# 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=outter(index) index做outter的参数(最原始那个index函数的内存地址) #等号左边index=wrapper函数的内存地址
home=outter(home) home做outter的参数(最原始那个home函数的内存地址) #等号左边home=wrapper函数的内存地址
#===============================装饰器==================================
home('egon') 相当于wrapper('egon')
index() 相当于wrapper()
三、装饰器的语法糖
糖:黏黏的可以把两个东西粘起来,
语法糖就是使用相应的语法奖装饰器和被装饰的函数连接起来,需要装饰器的时候直接调用就可以,不用每次都写例如index=outter(index)这样的语句.
@装饰器的名字: 这个语法糖就相当于帮我们做了index=timmer(index)这么一件事情
用法:要在被装饰对象正上方单独一行写上(该行只能写这一样东西)
1.语法糖的使用:
import time
def timmer(func): # func=最原始那个函数的内存地址
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
stop=time.time()
print('run time is %s' %(stop-start))
return res
wrapper.__doc__=func.__doc__ 将func函数中的文档注释这个属性传给wrapper
wrapper.__name__=func.__name__ 将func函数最初对应函数名字这个属性传给wrapper
return wrapper 传这些属性,都是为了让被装饰后的函数和原函数各方面都一样,
语法糖的使用
@timmer #index=timmer(index) ##index=timmer(最原始那个index的内地址)
#index=wrapper函数的内地址
def index():
"""这是index功能"""
print('welcome to index page')
time.sleep(3)
return 123
@timmer #home=timmer(home) #index=timmer(最原始那个home的内地址)
#home=wrapper函数的内地址
def home(name):
"""这是home功能"""
print('welcome %s to home page' %name)
time.sleep(1)
# home('egon') #wrapper('egon')
# index() #wrapper()
# print(help(index)) help()括号中放入函数时可以查看函数中的注释
# print(help(home))
# print(index.__doc__)
print(index.__name__)
2.wraps的使用:
在使用装饰器装饰函数时,为了让装饰后的函数和原函数一模一样,需要将原函数的各个属性传给装饰后的函数,一个函数具有多个属性,一个一个的传会让代码冗余,这时候用到 Python自带的模块wraps,wraps相当于是一个装饰器,它的功能就是将装饰后的函数和原函数各个属性都保持一致,它的使用方法是在装饰器函数的正上方使用@wraps()这个括号里面必须要传例子中的"func"这样的参数来调用
from functools import wraps 首先从功能模块中调用wraps模块
import time
def timmer(func): # func=最原始那个home的内地址
@wraps(func) wraps的使用方式
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():
"""这是index功能"""
print('welcome to index page')
time.sleep(3)
return 123
@timmer
def home(name):
"""这是home功能"""
print('welcome %s to home page' %name)
time.sleep(1)
print(help(index))
print(index.__name__)
四、有参装饰器
1.无参装饰器的模板
----------------------------------------------------------------------------无参装饰器模板------------------------------------------------------------------------
def outter(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
有参装饰器和无参装饰器的区别对比
需求简介:
模拟网站登录之后才能进行其他操作定义出来的装饰器,在装饰器函数中加入登录逻辑,可以判断认证数据来源,数据来源可以是'file'.'MySQL'.'ldap'等可以存放用户数据的类似数据库的数据来源
下面的程序是无参装饰器类型的:
import time
user_info={'current_user':None}
def auth(func):
def wrapper(*args,**kwargs):
if user_info['current_user'] is not None:
res=func(*args,**kwargs)
return res
inp_user=input('username>>>: ').strip()
inp_pwd=input('password>>>: ').strip()
if inp_user == 'egon' and inp_pwd == '123':
# 记录登录状态
user_info['current_user']=inp_user
print('login successful')
res=func(*args,**kwargs)
return res
else:
print('user or password error')
return wrapper
@auth
def index():
"""这是index功能"""
print('welcome to index page')
time.sleep(2)
return 123
@auth
def home(name):
"""这是home功能"""
print('welcome %s to home page' %name)
time.sleep(1)
index()
home('egon')
有参装饰器模板
---------------------------------------------------------------------------------------有参装饰器模板-------------------------------------------------------------
def outter2(xxx,yyy):
def outter(func): 有参装饰器相对于无参装饰器来说,就是在装饰器函数体中有对新参数的使用需求,
def wrapper(*args,**kwargs): 但是wrapper和outter的参数又固定了不能随便改动,所以使用参数传值的方法行不
res=func(*args,**kwargs) 通,使用给函数传值的另外一种方法,使用闭包函数来进行传值,这时候使用闭包函数
print(xxx) 的定义,外包一个函数,装饰器函数体需要的参数传进去就可以了,这相对于无参装饰
print(yyy) 器来说外层多了其他的参数,这样的装饰器称为有参装饰器.
return res
return wrapper
return outter
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
import time
user_info={'current_user':None}
def auth2(engine='file'):
def auth(func):
def wrapper(*args,**kwargs): 装饰器函数里边需要一个新的engin参数来判断是那种认
if user_info['current_user'] is not None: 证来源,这时候就需要用到有参装饰器了
res=func(*args,**kwargs)
return res
inp_user=input('username>>>: ').strip()
inp_pwd=input('password>>>: ').strip()
if engine == 'file':
print('基于文件的认证')
if inp_user == 'egon' and inp_pwd == '123':
# 记录登录状态
user_info['current_user']=inp_user
print('login successful')
res=func(*args,**kwargs)
return res
else:
print('user or password error')
elif engine == 'mysql':
print('基于mysql数据的认证')
elif engine == 'ldap':
print('基于ldap的认证')
else:
print('无法识别认证源')
return wrapper
return auth
@auth2(engine='mysql') # @auth ===> index=auth(最原始那个index的内存地址)===》index=wrapper
def index():
"""这是index功能"""
print('welcome to index page')
time.sleep(2)
return 123
@auth2(engine='file')
def home(name):
"""这是home功能"""
print('welcome %s to home page' %name)
time.sleep(1)
index() #wrapper()
home('egon')
五、global和nonlocal(了解知识点)
x=1
def func():
x=2
func() 在局部作用域修改全局变量的值,对于不可变类型是不行的
print(x) 这里的x值还还为1
x=[]
def func():
x.append(1)
x.append(2)
x.append(3)
func() 在局部作用域修改全局变量的值,对于可变数据类型是可以的
print(x) x在这里就等于:x=[1,2,3]
想要在局部作用域中修改全局变量可以使用global来实现
global: 在局部声明变量是全局变量
x=1
def func():
global x 在这里注明x是全局变量 接下来x=2对x进行修改
x=2
func()
print(x) 这里输出的x=2
nonlocal:在局部声明变量是外层函数的变量 ,当外层函数不知一层时,使用nonlocal声明之后,它会一层一层的往外去找声明的变量,但是仅限于局部变量,不会到全局变量中寻找,当在局部中找不到匹配的外层函数变量时,程序就会进行报错.
x=333
def f1():
x=222
def f2():
x=111
def f3():
nonlocal x 比如这里使用nonlocal声明之后,修改的是f2下面的x=111这个x,将其值改为0
x=0 机制就是从f3声明开始,依次在f2中、f1中寻找可用的局部变量,不会修改作为
f3() 全局变量的x=333的值
print('f2内部的x: ',x)
f2()
print('这是f1内部的x: ',x)
f1()
print(x)