目录
1. 装饰器基础
2. 装饰器编码
3. @修饰符
4. 多层装饰器
1. 装饰器基础
返回值为另一个函数的函数,通常使用 @wrapper 语法形式来进行函数变换。 装饰器的常见例子包括 classmethod() 和 staticmethod()。
装饰器语法只是一种语法糖,以下两个函数定义在语义上完全等价:
def f(arg):
...
f = staticmethod(f)
@staticmethod
def f(arg):
...
2. 装饰器编码
Python中,装饰器(decorator)的功能是将被装饰的函数当做参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的被装饰的函数。
装饰器是为了解决新增功能、修改函数时,不要修改原代码、增加复杂逻辑的需求而出现的;
通过闭包实现新功能,同时将原函数作为外部函数参数调用,并将原函数重新声明为闭包返回结果,这样原函数作为外部作用域不被修改,同时重新声明的原函数通过闭包实现新功能;
如下示例中,按装饰器编码思路如下:
- 初始函数为 test() ,要给 test() 新增注释功能,但不想修改 test() 代码;
- 新增 log() 作为装饰器,以 funx 作为形参,并在闭包内部函数 closure_test 中增加需要新增的注释功能,并调用 funx() ;
- 将初始函数 test 作为参数传递给装饰器 log() ,同时赋值给初始函数同名 test 为新函数;
- 初始函数 test() 作为装饰器外部作用域实参,保持不变,不被新赋值的同名 test() 函数修改;
- 新赋值函数 test() 则赋值为装饰器返回结果,调用未被修改的初始函数 test() ,并实现了需要的注释功能。
def log(funx):
def closure_test():
print(f'开始函数{funx.__name__}调用!')
funx()
print(f'结束函数{funx.__name__}调用!')
return closure_test
def test():
print('开始测试~~~')
test = log(test)
test()
开始函数test调用!
开始测试~~~
结束函数test调用!
3. @修饰符
上一章节中test = log(test) 有更简洁的表达方法,即应用@修饰符形成的“@语法糖”来解决这个问题,把“@修饰符+装饰器”,置于被修饰的原函数之前;
上一章节代码可以按如下形式编写:
def log(funx):
def closure_test():
print(f'开始函数{funx.__name__}调用!')
funx()
print(f'结束函数{funx.__name__}调用!')
return closure_test
@log
def test():
print('开始测试~~~')
test()
开始函数test调用!
开始测试~~~
结束函数test调用!
如果原函数 test() 有参数,则装饰器内部函数也添加参数,并将该参数录入内部函数中被调用的初始函数,实现如下:
def log(funx):
def closure_test(testname):
print(f'开始函数{funx.__name__}调用!')
funx(testname)
print(f'结束函数{funx.__name__}调用!')
return closure_test
@log
def test(testname):
print(f'开始测试{testname}~~~')
test('CS 1.6')
开始函数test调用!
开始测试CS 1.6~~~
结束函数test调用!
为了防止初始函数 test() 后续修改形参数量,可以装饰器内部函数参数设计为收集参数-元组,如下:
def log(funx):
def closure_test(*testname):
print(f'开始函数{funx.__name__}调用!')
funx(*testname)
print(f'结束函数{funx.__name__}调用!')
return closure_test
@log
def test(var1,var2):
print(f'开始测试{var1},{var2}~~~')
test('DOTA','LOL')
开始函数test调用!
开始测试DOTA,LOL~~~
结束函数test调用!
4. 多层装饰器
Python也支持装饰器多层嵌套;
多个@装饰器上下排列,相当于从上至下从最外层到最内层嵌套函数,示例如下:
#三个装饰器
def addone(funx):
def closure_test(*input):
x = funx(*input)
return x + 1
return closure_test
def reducetwo(funx):
def closure_test(*input):
x = funx(*input)
return x - 2
return closure_test
def square(funx):
def closure_test(*input):
x = funx(*input)
return x * x
return closure_test
#不使用@修饰符,直接做多重函数嵌套
def test1(numble):
return numble
test1 = square(reducetwo(addone(test1)))
print(test1(5))
#使用@修饰符,做多重装饰器
@square
@reducetwo
@addone
def test2(numble):
return numble
print(test2(5))
#返回结果一致
16
16


