装饰器
在python中有一个特殊的符号 @ ,使用此符号的时候表示用后边的函数作为装饰器来装饰下面的函数,并改变下面函数的引用。
在装饰器中大至分为两种,一种是直接使用函数引用的装饰器,与直接使用函数的装饰器。
不管使用上面两种的哪一种装饰器,都遵循,只要代码读取到@符号并已经将一个函数引用传递给该修饰器的时候,则首先进行最外层函数的运行。
def w1(func1):
print("---正在装饰1---")
def inner1():
print("--正在验证权限1---")
func1()
return inner1
@w1
def f1():
print("---f1---")
f1()
其输出结果为:
---正在装饰1---
直接使用函数引用的装饰器
在使用直接使用函数引用的装饰器的时候,需要在@符号的时候,后面直接跟函数名而不加括号与参数: @func 。
在使用的时候,读取到@符号的时候将会将被修饰的函数的引用作为第一个参数传递到修饰函数中,并运行第一层函数,将被修饰函数的函数名变量等于第一层函数的返回值。
如:
def w1(func1):
print("---正在装饰1---")
def inner1():
print("--正在验证权限1---")
func1()
return inner1
@w1
def f1():
print("---f1---")
f1()
其输出结果为
---正在装饰1---
--正在验证权限1---
---f1---
其上边的代码相当于下面的代码:
def w1(func1):
print("---正在装饰1---")
def inner1():
print("--正在验证权限1---")
func1()
return inner1
#@w1
def f1():
print("---f1---")
f1 = w1(f1)
f1()
输出结果是一样的。
代码解读:首先将原来的f1函数的引用作为参数func1的值传递进去,此时func1=f1,在将最外层函数的返回值(inner1的引用),在赋值给f1。此时运行f1时相当于运行inner1函数。
直接使用函数引用装饰器对有参数和无参数函数进行装饰。
对于装饰无参数的函数与上边直接使用函数引用装饰器的装饰器的方法是相同的,在此不再做介绍与解释。
对于装饰有参数的函数与装饰无参数的时候使用与定于的时候是有所区别的。
如:
def w1(func1):
print("---正在装饰1---")
def inner1():
print("--正在验证权限1---")
func1()
return inner1
@w1
def f1(a,b):
print("---f1--a=%d--d=%d---"%(a,b))
#f1 = w1(f1)
f1(1,2)
此时运行时就会报错:
代码解读:
利用上边讲解的原理可以知道,运行完装饰器的函数的f1是等于inner1函数的,而inner1函数是没有参数的,如果还对f1函数进行传值的话,就会报错。
代码修改:
def w1(func1):
print("---正在装饰1---")
def inner1(a,b):
print("--正在验证权限1---")
func1(a,b)
return inner1
@w1
def f1(a,b):
print("---f1--a=%d--d=%d---"%(a,b))
#f1 = w1(f1)
f1(1,2)
此时的结果为:
---正在装饰1---
--正在验证权限1---
---f1--a=1--d=2---
代码解读:
利用上边的原理可以知道,f1=inner1,func1=原来的f1函数,此时对f1传值等于对inner1传值,inner1有对应的参数,可以接受,再传值给func1函数即可。
设置一个不定长参数的装饰器
如果我们不知道需要修饰的函数的参数的数目与具体值,则可以设置不定长的参数的参数进行传递,在python中可以表示不定长参数的参数为:参数前加*,或者参数前加**。
代码修改:
def w1(func1):
print("---装饰器1---")
def inner1(*args,**kwargs):
print("--正在验证权限1---")
func1(*args,**kwargs)
return inner1
@w1
def f1(a,b):
print("---f1--a=%d--d=%d---"%(a,b))
@w1
def f2(a,b,c):
print("---f1--a=%d--d=%d--c=%d-"%(a,b,c))
#f1 = w1(f1)
f1(1,2)
print("\n---分割线----\n")
f2(1,2,3)
运行结果为:
---装饰器1---
---装饰器1---
--正在验证权限1---
---f1--a=1--d=2---
---分割线----
--正在验证权限1---
---f1--a=1--d=2--c=3-
代码部分解读:
在给func1传参的时候,使用func1(*args,**kwargs),而不使用func1(*args,**kwargs),是因为,在给inner1传递参数的时候,由于使用的是不定长参数,所以args此时为一个元祖,kwargs此时为一个字典,如果直接传递则相当于传递了两个参数,一个元组,一个字典,*args,**kwargs相当于将其解封为最原来的形式进行传递,既传递的参数的数量与引用完全相同。
直接使用函数引用装饰器对有返回值和无返回值函数进行装饰。
对于对无返回值的参数进行修饰的时候,与上边的举例是相同的,在此不再做举例与解释。
对有返回值的函数进行修饰的时候是略有不同的。
如:
def w1(func1):
print("---装饰器1---")
def inner1():
print("--正在验证权限1---")
func1()
return inner1
@w1
def f1():
print("---f1-----")
return "我是返回值"
#f1 = w1(f1)
ret = f1()
print(ret)
输出结果为:
---装饰器1---
--正在验证权限1---
---f1-----
None
输出的结果似乎与我们设想的有所不同,返回值似乎消失了?
代码解读:
由上可知此时的f1等于inner1,func1等于原来的f1,在inner1里面调用了有返回值的func1函数,但是inner1函数并没有返回值,此时ret被赋值为None。
代码修改:
def w1(func1):
print("---装饰器1---")
def inner1():
print("--正在验证权限1---")
ret = func1()
return ret
return inner1
@w1
def f1():
print("---f1-----")
return "我是返回值"
#f1 = w1(f1)
ret = f1()
print(ret)
输出结果为:
---装饰器1---
--正在验证权限1---
---f1-----
我是返回值
此时就符合我们的预想了。
此时的修饰器不管f1函数有没有返回值都能用,不过没有返回值是inner1返回值为None
通用修饰器
结合以上的知识则可以设计一个通用的修饰器。
代码为:
def w1(func1):
print("---装饰器1---")
def inner1(*args,**kwargs):
print("--正在验证权限1---")
ret = func1(*args,**kwargs)
return ret
return inner1
@w1
def f1():
print("---f1-----")
return "我是返回值"
@w1
def f2(a,b,c):
print("---f1--a=%d--d=%d--c=%d-"%(a,b,c))
return "我是返回值2号"
#f1 = w1(f1)
ret = f1()
print(ret)
print("\n---分割线----\n")
ret = f2(1,2,3)
print(ret)
输出示例:
---装饰器1---
---装饰器1---
--正在验证权限1---
---f1-----
我是返回值
---分割线----
--正在验证权限1---
---f1--a=1--d=2--c=3-
我是返回值2号
两个或两个以上多个使用函数引用的修饰器
在使用 两个或两个以上多个使用函数引用的修饰器的时候,由于代码是从上往下读取的,所以在读取到第一个修饰器的时候,首先需要一个函数引用进行传递,继续向下读,读取到第二个修饰器,需要一个函数引用进行传递,继续向下读,读到被修饰的函数,将被修饰的函数传递给第二个修饰器,并改变被修饰函数的引用,再将此时的被修饰函数传递给第一个修饰器,再次改变被修饰函数的引用。
def w1(func1):
print("---装饰器1---")
def inner1():
print("--正在验证权限1---")
ret = func1()
return inner1
def w2(func2):
print("---装饰器2---")
def inner2():
print("--正在验证权限2---")
func2()
return inner2
@w1
@w2
def f1():
print("---f1-----")
#f1 = w1(f1)
f1()
输出结果为:
---装饰器2---
---装饰器1---
--正在验证权限1---
--正在验证权限2---
---f1-----
代码解析:
python解释器再运行到@w1的时候,等待传递一个传递的函数引用,运行到@w2的时候,等待传递一个传递的函数引用,继续向下运行读取到def f1,将f1函数的引用传递给第二个修饰器@w2,运行w2最外层函数,此时func2 = 原来的f1函数,f1 改变引用为inner2函数,然后再将此时的f1传递@w1,此时func1 = f1 = inner2,f1 = inner1。然后调用此时的f1(inner1)。
直接使用函数的装饰器
直接使用函数的装饰器,在使用语法上边与使用函数引用的装饰器不同的是,再@函数名后加括号与相应的参数:@func(*args,**kwargs)
直接使用函数的装饰器在使用的时候,是先运行func(*args,**kwargs)这个函数,然后将返回的函数引用与@符号结合,结合之后的用法与直接使用函数引用的用法一致。
如:
def test(temp):
print("----test---temp=%d--"%temp)
def w1(func1):
print("---装饰器1---")
def inner1():
print("--正在验证权限1---")
ret = func1()
return inner1
return w1
@test(666)
def f1():
print("---f1-----")
f1()
输出结果为:
----test---temp=666--
---装饰器1---
--正在验证权限1---
---f1-----
代码解析:
@test(666)的时候,首先运行test(666)函数,打印语句,并返回w1函数引用,然后w1函数引用雨@结合为@w1,此时将f1的引用传递给w1,此时f1=inner1,func1 = 原来的f1,然后调用此时的f1.