想要弄懂Python中的装饰器,首先需要知道什么是闭包,比较正式的说法:将组成的函数语句和这些语句的执行环境(所需变量等)打包在一起,得到的对象称之为闭包。
构成闭包必备条件:①必须要有内嵌函数、②内嵌函数必须外部嵌套函数中的变量;③外部嵌套函数的返回值必须是内嵌函数名。
程序应该遵循开放封闭原则,虽然这个原则用于的面向对象开发,但是也适用函数式编程,简单来说,它规定了已经实现的功能代码不允许被修改,但是可以被扩展,也即:封闭(已实现的功能代码块)、开放(对扩展开发)。
装饰器:在不用修改原函数代码情况下,就可以给原函数(在函数整体之前或之后)添加新功能(不能实现在原函数内部添加新功能),并且在调用的时候和原来调用一模一样,这里需要注意的是,装饰器是基于闭包环境存在的。
下面就通过代码讲解装饰器在各种场景中的应用:
情况1:对没有参数没有返回值的函数装饰
# ============= 闭包环境 ============ #
def set_function(fun):
def call_function():
print('---权限验证---')
fun()
return call_function
# =================================== #
@set_function
def test1():
print('---test1---')
test1()
# 输出结果:
# ---权限验证---
# ---test1---
# 说明:@set_function 等价于:test1 = set_function( test1)
# 需要注意的是:只要执行到@set_function的时候,其实就已经开始装饰了。
# 而不是到了test1()的时候才开始装饰,也就是说装饰器在函数被调用之前就已经装饰完毕了。
情况2:对有参数无返回值的函数装饰
def set_function(fun):
def call_function(a):
print('---权限验证---')
fun(a)
return call_function
@set_function
def test1(num):
print('---test1---:%d' %num)
test1(100)
# 输出结果:
# ---权限验证---
# ---test1---:100
# 说明:100传递给call_function(a)的形参a,fun(a)将a传递给test1(num)中的形参num。
情况3:对带有不定长参数或缺省参数装饰
def set_function(fun):
print('---开始进行装饰---')
def call_function(*args, **kwargs):
print('---权限验证---')
fun(*args, **kwargs)
return call_function
@set_function
def test1(num, *args, **kwargs):
print('---test1---')
print(num, args, kwargs)
test1(100)
test1(100, 200)
test1(100, 200, m=300)
# 输出结果:
# ---开始进行装饰---
# ---权限验证---
# ---test1---
# 100 () {}
# ---权限验证---
# ---test1---
# 100 (200,) {}
# ---权限验证---
# ---test1---
# 100 (200,) {'m': 300}
情况4:对带有返回值的函数装饰(1)
def set_function(fun):
print('---开始进行装饰---')
def call_function(*args, **kwargs):
print('---权限验证---')
fun(*args, **kwargs)
return call_function
@set_function
def test1(num, *args, **kwargs):
print('---test1---')
print(num, args, kwargs)
return '200','ok'
ret = test1(100)
print(ret)
# 输出结果:
# ---开始进行装饰---
# ---权限验证---
# ---test1---
# 100 () {}
# None
情况5:对带有返回值的函数装饰(2)
def set_function(fun):
print('---开始进行装饰---')
def call_function(*args, **kwargs):
print('---权限验证---')
return fun(*args, **kwargs)
return call_function
@set_function
def test1(num, *args, **kwargs):
print('---test1---')
print(num, args, kwargs)
return '200','ok'
# ret = test1(100)
# print(ret)
# 输出结果:
# ---开始进行装饰---
# ---权限验证---
# ---test1---
# 100 () {}
# ('200', 'ok')
# 说明:
# return None 相当于 None 也相当于函数没有返回值(无return语句)。
情况6:多个装饰器对同一个函数装饰
def set_function1(fun):
print('---开始进行装饰1---')
def call_function1(*args, **kwargs):
print('---权限1验证---')
return fun(*args, **kwargs)
return call_function1
def set_function2(fun):
print('---开始进行装饰2---')
def call_function2(*args, **kwargs):
print('---权限2验证---')
return fun(*args, **kwargs)
return call_function2
@set_function1
@set_function2
def test1(num, *args, **kwargs):
print('---test1---')
print(num, args, kwargs)
# ret = test1(100)
# print(ret)
# 输出结果:
# ---开始进行装饰2---
# ---开始进行装饰1---
# ---权限1验证---
# ---权限2验证---
# ---test1---
# 100 () {}
# None
# 意义:对同一个函数可以加多种不同的功能,并且将功能分散在各个装饰器中。
下面针对情况6,给出函数相互之间的调用顺序,当执行至print(ret):
step1:程序首先会执行set_function2(fun)函数,此时fun接受的实参是函数名test1;
step2:print(’—开始进行装饰2—’)
step3:然后执行set_function1(fun)函数,此时fun接受的实参是函数名test1;
step4:print(’—开始进行装饰1—’)
step5:执行call_function1()函数,print(’—权限1验证—’), 在实际工程中,可以在这里添加 你的业务代码块;
step6:执行call_function2()函数,print(’—权限2验证—’) , 在这里添加业务代码块;
step7: 最后执行test1()函数,print(’—test1—’).
情况7:应用装饰器类对函数进行装饰
class Test(object):
def __init__(self, func):
self.function = func
def __call__(self):
print('--此处添加额外功能--')
return self.function
@Test # get_str = Test(get_str)
def get_str():
return 'ok'
ret=get_str() # 调用__call__方法。ret 此时绑定self.function所绑定的func。
print(ret())
# 输出结果:
# --此处添加额外功能--
# ok
情况8:带有参数的装饰器对函数进行装饰
def set_level(level_num):
def set_func(func):
def call_func(*args, **kwargs):
if level_num == 1:
print('----验证权限1----')
elif level_num == 2:
print('----验证权限2----')
return func()
return call_func
return set_func
@set_level(1)
def test1():
print('----test1----')
return 'ok'
@set_level(2)
def test2():
print('----test2----')
return 'bye'
test1()
test2()
# 输出结果:
# ----验证权限1----
# ----test----
# ----验证权限2----
# ----test2----
# 说明:
# 当运行至@set_level(1)时,程序会将1当做实参传递,并且将用set_level函数的返回值对test1
# 函数进行装饰,最终test1也指向call_func函数。