Last Updated on

Python中的闭包是一个比较模糊的概念,不太好理解,我最近的面试中也被问及,在一个单例模式的实现上,我用装饰器实现单例,然后面试官就问到了我对闭包的理解,回答的不太清楚。

所以,回来后,好好的查资料理解了一下闭包的概念。下面我们就一起来试着理解这个概念,这在对Python的不断学习中,也是一个避不开的概念。希望对大家有用。

闭包

首先,闭包并不只是一个python中的概念,在函数式编程语言中基本都有应用。

闭包是引用了自由变量的函数,这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。

所以简单的说,就是函数定义中引用了函数外定义的变量,并且该函数可以在其定义环境外被执行,相同的函数实例在执行中此外部定义的变量是共享的。然后此外部定义的变量又是外部函数的内部变量,外部函数返回此定义的内部函数,这样就形成了一个闭包。

看下面示例:

def a():
    var = []
    def b(para):
        var.append(para)
        print(var)
    return b

s = a()
s(1)
s(2)

# 输出结果:
[1]
[1, 2]
[3]
[3, 4]

如上,可以看到,内部函数b应用了外部变量var,在函数b中修改外部变量var,并打印出来,然后外部函数返回此内部函数。

通过外部函数,生成内部函数实例s,运行s函数,会发现外部变量var的值是可以保存和共享的。但是我们再次生成新的函数实例s2,会发现s2中的var变量和s的是不相关的,此外部变量是跟函数实例相关的。

如此,基本可以得到:

  • 闭包中的引用的自由变量只和具体的闭包有关联,闭包的每个实例引用的自由变量互不干扰。
  • 一个闭包实例对其自由变量的修改会被传递到下一次该闭包实例的调用。

而创建一个闭包,基本也要满足以下几点:

  • 必须有内嵌函数
  • 内嵌函数必须引用外部变量
  • 外部函数必须返回内嵌函数

到此, 你是不是发现这个跟什么很相似,是的,就是装饰器!

Python装饰器就是闭包概念的一种体现

这也就解释了为什么面试官在看到我用装饰器实现单例的时候,会问我闭包。因为这就是很典型的闭包的,看下面单例模式的装饰器:

from functools import wraps

def Singleton(cls):
    instance = {}
    @wraps(cls)
    def wrapper(*args, **kwargs):
        if cls not in instance:
            instance[cls] = cls(*args, **kwargs)
        return instance[cls]
    return wrapper

其中,外部变量instance就是被内部函数wrapper引用,外部函数最后返回内部函数,就是一个典型的闭包。每个被此装饰器装饰后的类,都会有一个独有的instance变量来保存实例,因为闭包中每个实例引用的自由变量instance互不干扰,所以能实现单例模式。

对单例模式不熟悉的,请看:《Python 单例模式》

OK,大致闭包的概念就是如此,理解闭包主要是要理解其中的设计思想,这样才能更好的使用闭包。

有任何问题,欢迎留言