python装饰器,细致讲解
1.储备知识
-
*args,**kwargs
def index(x,y):
print(x,y)
def wrapper(*args,**kwargs):#接收参数
func(*args,**kwargs)#将参数打散 -
名称空间与作用域:名称空间的“嵌套”是在函数定义阶段,即检测语法的时候确定的
-
函数对象:
-
可以把函数当做参数传入
-
可以把函数当做返回值返回
-
-
def outter(func):
def wrapper():
pass
return wrapper -
闭包函数
-
函数B在函数A内部,并且函数B调用外层函数作用域的变量,那么函数B就是闭包函数
-
def funcA():
x=111
def funcB():
x
return funcB
-
-
传递函数的方式
-
通过参数的形式为函数传值
-
通过闭包的方式为函数传值
-
2.装饰器
-
什么是装饰器
-
定义一个函数,该函数在不修改原函数代码并且不改变调用方式的前提现,为被装饰的对象增加额外的功能
-
-
为何要用装饰器
-
开放封闭原则
-
开放:指的是对拓展功能是开放的
-
封闭:指的是对修改源代码是封闭的
-
-
3.得到装饰器思路
需求:在不修改index函数源代码以及调用方式的情况下为其添加统计时间的功能
def index(x,y):
time.sleep(3)
print('index %s %s'%(x,y))
index(111,222)
index(111,y=222)
index(x=111,y=222)
方案一:直接在index函数内加统计时间的代码
import time
def index(x,y):
start = time.time()
time.sleep(3)
print('index %s %s'%(x,y))
stop = time.time()
print(stop-start)
index(111,333)
问题:没有修改代码的调用方式,但是修改了源代码,方案一失败
方案二:在函数调用的时候添加统计时间的代码
def index(x,y):
time.sleep(3)
print('index %s %s'%(x,y))
start = time.time()
index(111,222)
stop = time.time()
print(stop-start)
start = time.time()
index(333,444)
stop = time.time()
print(stop-start)
start = time.time()
index(555,666)
stop = time.time()
print(stop-start)
问题:没有修改index的源代码,也没有修改调用方式,并且加上了新功能,但是代码冗余。方案二失败
方案三:将方案二中冗余的代码部分写成函数
def index(x,y):
time.sleep(3)
print('index %s %s'%(x,y))
def swapper():
start = time.time()
index(111,222)
stop = time.time()
print(stop-start)
swapper()
问题:解决了代码冗余问题,但是函数的调用方式发生改变。方案三失败
方案三优化一:将index的参数写活
def index(x, y):
time.sleep(3)
print('index %s %s' % (x, y))
def swapper(*args,**kwargs):
start = time.time()
index(*args,**kwargs)
stop = time.time()
print(stop - start)
swapper(2222,54544)
方案三优化二:在优化一的基础上把被装饰的对象写活,原来只能装饰index
def index(x,y,z):
time.sleep(3)
print('index %s %s %s' %(x,y,z))
def home(name):
time.sleep(2)
print('welcome %s to home page' %name)
def outter(func):
# func = index的内存地址
def wrapper(*args,**kwargs):
start=time.time()
func(*args,**kwargs) # index的内存地址()
stop=time.time()
print(stop - start)
return wrapper
index=outter(index) # index=wrapper的内存地址
home=outter(home) # home=wrapper的内存地址
方案三优化三:给wrapper添加返回值,达到以假乱真
def index(x,y,z):
time.sleep(3)
print('index %s %s %s' %(x,y,z))
def home(name):
time.sleep(2)
print('welcome %s to home page' %name)
def outter(func):
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
stop=time.time()
print(stop - start)
return res
return wrapper
# 偷梁换柱:home这个名字指向的wrapper函数的内存地址
# home=outter(home)
#
#
# res=home('egon') # res=wrapper('egon')
# print('返回值--》',res)
4.语法糖
import time
装饰器
def timmer(func):
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
stop=time.time()
print(stop - start)
return res
return wrapper
# 在被装饰对象正上方的单独一行写@装饰器名字
# @timmer # index=timmer(index)
def index(x,y,z):
time.sleep(3)
print('index %s %s %s' %(x,y,z))
# @timmer # home=timmer(ome)
def home(name):
time.sleep(2)
print('welcome %s to home page' %name)
index(x=1,y=2,z=3)
home('egon')
2.有参装饰器
1.知识储备
-
由于语法糖@的限制,outter函数只能有一个参数,并且该参数只能接受被装饰对象的内存地址
def outter(func):
# func = 被装饰函数的内存地址
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
return res
return wrapper
-
index被装饰后(偷梁换柱之后)
-
index的参数什么样子,wrapper的参数就应该什么样子
-
index的返回值什么样子,wrapper的返回值就应该什么样子
-
index的属性什么样子,wrapper的属性就应该什么样子==》from functools import wraps
-
2.有参装饰器
def auth(db_type):
def deco(func):
def wrapper(*args, **kwargs):
name = input('your name>>>: ').strip()
pwd = input('your password>>>: ').strip()
if db_type == 'file':
print('基于文件的验证')
if name == 'egon' and pwd == '123':
res = func(*args, **kwargs) # index(1,2)
return res
else:
print('user or password error')
elif db_type == 'mysql':
print('基于mysql的验证')
elif db_type == 'ldap':
print('基于ldap的验证')
else:
print('不支持该db_type')
return wrapper
return deco
3.有参装饰器模板
def 有参装饰器(x,y,z):
def outter(func):
def wrapper(*args, **kwargs):
res = func(*args, **kwargs)
return res
return wrapper
return outter