python中闭包的使用和闭包与延迟绑定


Python闭包和装饰器是经常涉及的问题,也是比较让人容易进入误区的地方,借此我说一下自己对Python闭包的一些理解

闭包必须有三个条件:

  1. 必须有一个内嵌函数
  2. 内嵌函数必须引用外部嵌套函数中的变量
  3. 外部函数返回值必须是内嵌函数

一个简单的事例:

def make_power(x):

    # 内嵌函数,引用外部函数的变量
    def power(y):
        return x ** y
    
    # 返回值是内嵌函数
    return power


pow2 = make_power(2)
#25
print("5的平方:",pow2(5))		
pow3 = make_power(3)
#125
print("5的立方:",pow2(5))




在Python中函数是一等公民:

  1. 函数本身可以赋值给变量,赋值后变量绑定函数
  2. 允许将函数本身作为参数传入另一个函数
  3. 允许函数返回一个函数

使用闭包的优点:

  1.     避免使用全局变量
  2.     可以提供部分数据的隐藏
  3.     可以提供更优雅的面向对象实现

闭包的应用场景:

   根据不同的参数,使用相同的实现去返回对应不同的结果,比如, 根据不同的配置获取不同的数据库连接, 或者要求在验证字符串长度时, 可以根据不同的最大长度验证(外层传递最大长度, 内层传递验证的字符串)。在抽象化一点, 外层定义了一个环境, 内层则是在该环境中的要做的事情, 但这个事情还没做,只是要做。


闭包与延迟与延迟绑定

先看一组代码

def fun():
    temp = [lambda x:i * x for i in range(4)]
    return temp

for t in fun():
    print(t(2), end=' ')



最后输出的结果是6 6 6 6

为什么最终会出现这个结果呢,这就是Python中闭包的延迟绑定的原因

对闭包延迟绑定的解释:

内函数引用外函数的一个局部变量,但是局部变量是循环的,那么最后内部引用的这个变量其实对于内函数来说是一个全局变量,所以每一次调用,其实内部使用的这个变量都是循环的最后一个值,而不是每次生成一个调用。其实不止循环,对变量修改也是一样的。所以很多人理解延迟绑定,最后一个才绑定。而本质上是不对的。

  来解释一下,内函数绑定的不是外函数的变量的值,而是地址空间,在外部函数返回以后发现返回的是内函数,这个自己的局部变量还在使用,不会清空这个地址区域而是保存这个地址区域,此时,这个地址空的值是最后一次修改这个变量之后的值,对于循环来说就是最后一个的值。

红色的部分充分解释了这个函数的输出结果的原因,其实,对于第一个temp列表存放的四个函数,也就是闭包当中的内嵌函数,内嵌函数的i只是内嵌函数的一个变量,而这个变量其实就是上面提到闭包三要素的第二条要素中的那个变量

下面再来一组函数帮助读者理解

def func():
    temp = []
    for i in range(4):     
        def f(x):
            return i * x
        temp.append(f)
    return temp



这是本人对上面函数理解之后,按照闭包三要素的形式写出来的,希望可以帮助大家理解