一.什么是装饰器
● 在不改变原有函数代码的前提下,为函数增添功能
·
二. 装饰器的用法
● 先定义要为函数增添的功能代码并封装在一个闭包(“闭包”详情见我上一条博客)里,在为要增添功能的函数上加上 @闭包函数名
例:
# 通用装饰器
def set_fun(fun):
def call_fun(*args, **kwargs):
print("要增添的功能")
# 当函数test()有返回值时,这里fun(*args, **kwargs)加return为了接收返回值并返回给call_fun(*args, **kwargs)
return fun(*args, **kwargs)
return call_fun
# 装饰器
@set_fun
def test():
print("test函数的功能")
test()
>>>
>要增添的功能
>test函数的功能
·
三. 装饰器的原理
def set_fun(fun):
def call_fun(*args, **kwargs):
print("要增添的功能")
return fun(*args, **kwargs)
return call_fun
@set_fun
def test():
print("test函数的功能")
test()
>>>
>要增添的功能
>test函数的功能
||(等价于)
def set_fun(fun):
def call_fun(*args, **kwargs):
print("要增添的功能")
return fun(*args, **kwargs)
return call_fun
def test():
print("test函数的功能")
test = set_fun(test)
test()
>>>
>要增添的功能
>test函数的功能
·
解析:
从图中可以看出,图中的test已经指向了函数set_fun(test)
,所以最下面执行的 test() 调用的已经不是def test()
里的内容,而是def set_fun(test)
里的内容。而call_fun()
函数里面的fun()
已经指向了def test()函数的内容。
因此
@set_fun 等价于 test = set_fun(test)
·
● 同一个闭包函数,对不同函数加装饰器,每一个装饰器都创建一个新的闭包内存空间,不会指向同一个闭包内存空间
例:
def set_fun(fun):
def call_fun(*args, **kwargs):
print("要添加的功能")
return fun(*args, **kwargs)
return call_fun
@set_fun
def test1(num):
print("test1的功能%d" % num)
@set_fun
def test2(num):
print("test2的功能%d" % num)
test1(100)
test2(200)
>>>
>要添加的功能
test1的功能100
要添加的功能
test2的功能200
解析:
如图,当给函数test2加上装饰器时,闭包函数set_fun()
并不会再去指向红色方框,而是会重新创建一个内存空间,指向绿色方框。
·
·
四. 多个装饰器对同一个函数装饰
代码实例:
def set_func_01(func):
print("這是裝飾器1")
def call_func(*args, **kwargs):
print("添加裝飾器1的功能")
return func(*args, **kwargs)
return call_func
def set_func_02(func):
print("這是裝飾器2")
def call_func(*args, **kwargs):
print("添加裝飾器2的功能")
return func(*args, **kwargs)
return call_func
@set_func_01 # 等價與test = set_func_01(test)
@set_func_02 # 等價與test = set_func_02(test)
def test():
print("---tesst---")
test()
实验结果:
·
解析:
注:当多个装饰器对同一个函数进行装饰时,在下面的先装饰,在上面的先运行功能
代码从上而下读取运行,当读到@set_func_01时,下面不是函数而是@set_func_02,所以代码会先跳过第一个装饰器,运行@set_func_02,发现下面是函数可以运行装饰器了
👇
·
·
实例演练:
代码:
def add_line(func):
def call_func(*args, **kwargs):
return "<td>"+func(*args, **kwargs)+"</td>"
return call_func
def add_title(func):
def call_func(*args, **kwargs):
return "<h1>"+func(*args, **kwargs)+"</h1>"
return call_func
@add_line
@add_title
def test():
return "haha"
print(test())
实验结果:
·
解析:
·
注:多个装饰器同时装饰一个函数时,在下面的先装饰,在上面的先调用
·
·
五. 对装饰器传入参数
当用同一个装饰器对不同函数进行装饰时,想让装饰器为不同的函数增加不同的功能,则需要给装饰器传参数解决
代码实例:
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(*args, **kwargs)
return call_func
return set_func
@set_level(1)
def test1():
print("----test1----")
@set_level(2)
def test2():
print("----test2----")
test1()
test2()
实验结果:
·
解析:
当对装饰器传入参数后,会将参数带入装饰器后用所返回的对象进行装饰。
所以这里需要在两层函数闭包外再加一层函数,用来传入参数并返回两层闭包函数的应用。