python基础语言之闭包

227 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

本文同时参与「掘力星计划」,赢取创作大礼包,挑战创作激励金

1. 函数引用

什么是函数的引用?我个人的理解就是将一个函数用“=”赋值给一个变量,那么这个变量跟所定义的函数在内存中指向的是同一个地址,即为函数的引用。下面看一个例子:

def test1():
    print('test1-----------')

#调用函数test1
test1()

#引用函数
ret = test1

#打印两个函数的地址
print(id(ret))
print(id(test1))

#调用函数ret
ret()

#运行结果
#调用函数test1输出结果
test1-----------

#打印两个函数的地址输出结果
140212571149040
140212571149040

#调用函数ret输出结果
test1-----------

上述代码中,首先定义一个函数test1,函数体打印一个字符串,接下来通过函数名加括号的形式调用函数即test1(),这时程序会执行函数体,结果会打印输出字符串test1-----------

接下来就是函数的引用,将test1通过“=”赋值给一个变量ret,这时并不会调用函数test1,而是让ret和test1指向同一个地址。这时再去调用ret函数时同样会输出字符串test1-----------

2. 什么是闭包

个人理解是:首先定义一个函数(外部函数),然后在该函数中在定义一个函数(内部函数),并且在内部函数中用到了外部函数中的变量(包括参数),最后外部函数将内部函数的引用作为返回值返回。那么将这个函数及用到的变量称为闭包。

下面看一个代码示例:

#定义一个函数
def test(number):
    
    #在函数内部再定义一个函数,并且这个函数用到了外部函数的变量(或参数)
    def test_in(number_in):
        print('in test_in 函数,number_in is %d ' % number_in)
        return number+number_in
    #将内部函数test_in的引用作为外部函数的返回值返回
    return test_in

#调用test函数传入参数20,并将test函数的返回值(一个函数的引用)赋值给ret
ret = test(20)

#调用ret并传入参数100,注意这里调用ret相当于调用的是test_in函数,参数则传递给number_in
print(ret(100))

运行结果:
in test_in 函数,number_in is 100
120

解释一下上述代码, 首先ret = test(20)是调用test函数,传递参数为20并将返回值赋值给ret. 那么当执行到test内部时遇到test_in, 这时又定义了一个函数test_in, 此时可以将此段代码理解为:

def test_in(number_in):
    print('in test_in 函数, number_in is %d' % number_in)
    return 20 + number_in

然后将test_in的引用用作为test函数的返回值返回,那么ret=test(20)就可以理解为ret = test_in, 而print(ret(100))也就相当于print(test_in(100)) 所以最后得到的结果就是120

3. 看一个闭包的实际例子

def line_conf(a,b):
    def line(x):
        return a*x + b
    return line

line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5))
print(line2(5))

在这个例子中,函数line与变量a,b构成闭包. 在创建闭包的时候, 我们通过line_conf的参数a,b说明了这两个变量的取值, 这样, 我们就确定了函数的最终形式 (y = x + 1 和 y = 4x + 5) 我们只需要变换参数a,b,就可以获得不同的直线表达函数. 由此我们可以看到,闭包也具有提高代码可以复用性的作用. 如果没有闭包我们需要每次创建直线函数的时候同时说明a,b,x. 这样我们就需要更多的参数传递, 也减少了代码的可移植性.

4. 总结

a. 闭包优化了变量, 原来需要类对象完成的工作, 闭包也可以完成.

b. 闭包的缺点: 由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,造成内存消耗.