装饰器储备知识
- 名称空间和作用域
- *args**kwargs作为形参和实参的作用
- 函数对象
- 函数嵌套
装饰器(无参装饰器)
- 定义:给代码添加额外功能的函数
- 作用:在不改变代码调用方式和代码内容的前提下,对代码增加额外的功能
- 代码的原则:开放封闭原则。
案例:
阶段一:增加代码执行时间的功能(直接修改了定义阶段函数体代码)
不符合开放封闭原则
import time
def func1(x):
start=time.time()
print("my name is %s,welcome to my home" %x)
time.sleep(2)
stop=time.time()
print(stop-start)
func1("lzz")
阶段二:增加代码执行时间的功能(直接修改调用阶段的函数体代码,多处调用都需要修改,代码冗余)
代码写死了,只能对执行时间只对func1起作用
import time
def func1(x):
print("my name is %s,welcome to my home" %x)
time.sleep(2)
start=time.time()
func1("lzz")
stop=time.time()
print(stop-start)
阶段三:增加代码执行时间功能(功能写死了,只能对func1做装饰,增加了对原函数的传参,但此时的函数已经不是func1)
import time
def func1(x):
print("my name is %s,welcome to my home" %x)
time.sleep(2)
def func2(*args,**kwargs):
start=time.time()
func1(*args,**kwargs)
stop=time.time()
print(stop-start)
func2()
阶段四:增加代码执行时间功能(可以指定对某函数进行装饰,但是此时的func1已经不是原来定义的func1了,很多属性不同)
import time
def func1(x):
print("my name is %s,welcome to my home" %x)
time.sleep(2)
def outter(f):
def func2(*args,**kwargs):
start=time.time()
f(*args,**kwargs)
stop=time.time()
print(stop-start)
return func2
func1=outter(func1)
func1("lzz")
阶段五:增加代码执行时间功能(使用语法糖,简洁语法)
import time
def outter(f):
def func2(*args,**kwargs):
"""装饰的函数"""
start=time.time()
f(*args,**kwargs)
stop=time.time()
print(stop-start)
return func2
@outter #等同于 func1=outter(func1) ;语法糖是在被装饰函数的上方添加@装饰器名称
def func1(x):
"""原始函数"""
print("my name is %s,welcome to my home" %x)
time.sleep(2)
func1("fffafrf")
print(func1,func1.__doc__)
阶段六:增加代码执行时间功能(使用wraps使得被装饰函数和装饰函数的属性完全一样,res同步的被装饰和装饰函数的返回值)
装饰器成功是因为它让被装饰函数的使用者无感知
from functools import wraps
import time
def outter(f):
@wraps(f)
def func2(*args,**kwargs):
"""装饰的函数"""
start=time.time()
res=f(*args,**kwargs)
stop=time.time()
print(stop-start)
return res
return func2
@outter #等同于 func1=outter(func1)
def func1(x):
"""原始函数"""
print("my name is %s,welcome to my home" %x)
time.sleep(2)
return "lzz"
res=func1("fffafrf")
print(func1,func1.__doc__,res)
装饰器模板
from functools import wraps
def outter(func):
@wraps(func)
def wrapper(*args,**kwargs):
"""此处可定义装饰器的功能"""
res=func(*args,**kwargs)
return res
return wrapper
装饰器(有参装饰器)
给函数传值的方式有两种:一种是函数参数直接传值,另一种是将参数包给被传值函数
**args,**kwargs已经用作给函数体传值(需要根据原函数定义的格式相同);
f用于为函数名传值,因为要用到语法糖,outter()只能传递固定的参数
如果函数体内还需要变量如何传参? 答:再包一层
#挑选不同的认证功能,认证通过则打印消息,否则报错
def decoration(way):
def outter(func):
def wrapper(*args,**kwargs):
name=input("请输入您的用户名:").strip()
passwd=input("请输入您的密码:").strip()
if way=="txt":
print("文本来源认证方式")
elif way == "mysql":
print("数据库来源认证")
elif way == "ldap":
print("ldap认证方式")
if name=="lzz" and passwd=="123456":
print("登陆成功")
res=func(*args,**kwargs)
return res
else:
print("登陆失败")
return wrapper
return outter
@decoration("mysql") #会先转换为@outter;可以给内部传递多个参数
def fun1(x,y):
print("%s 您好,您的账户余额为 %s" %(x,y))
@decoration("ldap")
def home(home):
print("homename is %s" %home)
fun1("lzz",100)
home("袁山")
多个装饰器加载和运行的顺序
def outter1(func1):
def wrapper1(*args,**kwargs):
print("装饰器1")
res=func1(*args,**kwargs)
return res
return wrapper1
def outter2(func2):
def wrapper2(*args,**kwargs):
print("装饰器2")
res=func2(*args,**kwargs)
return res
return wrapper2
def outter3(func3):
def wrapper3(*args,**kwargs):
print("装饰器3")
res=func3(*args,**kwargs)
return res
return wrapper3
@outter1 #wrapper2=outter1(wrapper2) wrapper2=wrapper1--->func1=wrapper2
@outter2 #wrapper3=outter2(wrappers3) wrappers=wrapper2--->func2=wrapper3
@outter3 #add=outter3(add) add=wrapper3 --->func3=add
def add(x,y):
print(x+y)
add(1,2)
结论: 如果一个函数上有多个语法糖,语法糖的加载顺序是自下而上,执行顺序是自上而下