闭包
- 定义函数跟定义变量一样,都是一个引用指向了变量值或者函数体存储的位置,所以如果将函数名通过等于号赋值给了另外一个变量名,那么此时该变量名就具有了调用该函数的能力。
- 在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用,这样就构成了一个闭包。下面通过代码展示闭包:
def test(number):
print("---1---")
def test_in(number_2):
print("---2---")
print(number+number_2)
print("---3---")
return test_in
ret = test(100)
print("-"*20)
ret(1)
ret(100)
ret(200)
- 运行输出结果如下。一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。从运行结果也可以看出这点。
---1---
---3---
--------------------
---2---
101
---2---
200
---2---
300
装饰器
def w1(func):
def inner():
print("---正在执行添加的代码---")
func()
return inner
def f1():
print("---f1---")
def f2():
print("---f2---")
f1 = w1(f1)
f1()
f2 = w1(f2)
f2()
- 输出结果如下。可以看出通过添加函数
w1
实现了不修改f1
和f2
函数本身而增加功能的效果,并且执行时所调用的函数名字依然不变。
------
------
------
------
- 以上代码是为了介绍装饰器原理,当真正使用装饰器时,代码应该如下:
def w1(func):
def inner():
print("---正在执行添加的代码---")
func()
return inner
@w1
def f1():
print("---f1---")
@w1
def f2():
print("---f2---")
f1()
f2()
- 以上代码的执行结果与前面相同,这说明了第一个装饰器
@w1
等价于执行了f1 = w1(f1)
,第二个装饰器@w1
等价于执行了f2 = w1(f2)
。
- 当同一个函数被两个装饰器装饰时,应该先装饰里层的,再装饰外层的。下面用代码进行说明:
def makeBold(fn):
print("---正在装饰1---")
def wrapped():
print("---1---")
return "" + fn() +""
return wrapped
def makeItalic(fn):
print("---正在装饰2---")
def wrapped():
print("---2---")
return "" + fn() + ""
return wrapped
@makeBold
@makeItalic
def test():
print("---3---")
return "hello world"
ret = test()
print(ret)
- 输出结果如下。从结果可以看出装饰时从内向外,调用时从外向内。
------
------
------
------
------
<><> <><>
- 当原函数需要传参时,装饰器(即闭包)的内函数以及内函数中调用原函数的部分都需要加上相同数量的参数。
- 当原函数有返回值时,装饰器(即闭包)的内函数也需要有返回值,且调用原函数的部分需要将原函数的返回值用一个变量进行接收。
- 为了使装饰器具有通用性,即能够装饰有参、无参、有返回值和无返回值的函数,直接在定义装饰器的时候就将不定长参数和返回值都写上,因为不定长参数包括了零个或零个以上的参数,而无返回值时相当于返回了
None
。
装饰器带参数
- 上面我们讲装饰器都是在一个函数上面装饰上一个函数名,但还有一种比较特殊的装饰器,那就是装饰器带有参数。下面写上代码进行展示:
def func_arg(arg):
def func(func_name):
def func_in():
print("---传入参数为:%s---"%arg)
if arg == "first_way":
func_name()
else:
func_name()
func_name()
return func_in
return func
@func_arg("first_way")
def test1():
print("---test1---")
@func_arg("second_way")
def test2():
print("---test2---")
test1()
test2()
---传入参数为:first_way---
---test1---
---传入参数为:second_way---
---test2---
---test2---
- 带有参数的装饰器可以理解为首先执行
func_arg("first_way")
这个函数,执行结果有个返回值,返回里面闭包的引用;
@
符号后面跟上闭包的引用,@func
,就相当于对下面的函数用闭包进行装饰,回到了我们熟悉的装饰器。
- 利用带参数的装饰器能够实现对不同函数实现不同的装饰效果。