page contents

Python 中的闭包(Closure)是什么,它的作用和优势是什么?

聊聊 Python 里闭包这回事儿,讲真的,这是个既简单又让人容易误会的东西。我刚开始接触的时候也没太在意,总觉得“闭包”这俩字听起来像啥高级玩意儿,实际上它在 Python 里特别常见,尤其是你开始写点函数式编程、装饰器或者是写个回调的时候,它就冒头了。

attachments-2025-07-fPV1gUFQ687068b521efa.jpg聊聊 Python 里闭包这回事儿,讲真的,这是个既简单又让人容易误会的东西。我刚开始接触的时候也没太在意,总觉得“闭包”这俩字听起来像啥高级玩意儿,实际上它在 Python 里特别常见,尤其是你开始写点函数式编程、装饰器或者是写个回调的时候,它就冒头了。

闭包到底是个啥?说白了,就是函数里套函数,然后里头那个函数还能记住外头函数的变量。举个栗子吧:

def outer(x):

    def inner(y):

        return x + y

    return inner


add_five = outer(5)

print(add_five(3))  # 输出 8

这个例子里,outer 是外函数,inner 是内函数。outer(5) 返回了 inner,而且这个 inner 还记住了 x=5。所以 add_five(3) 就相当于 5 + 3。这就叫闭包:函数里有函数,而且内部函数还能访问外部函数的变量,就算外部函数已经结束运行了,变量也不会丢。

那这玩意儿有啥用?不是多此一举吗?我一开始也这么想,后来才发现,这其实是 Python 里挺强大的一招,特别是在你要做“记忆”这种事的时候。

比如我想写个“计数器”函数,每次调用就加一:

def counter():

    count = 0

    def inc():

        nonlocal count

        count += 1

        return count

    return inc


c = counter()

print(c())  # 输出 1

print(c())  # 输出 2

你看,这就不需要全局变量,也不需要类,干干净净用个函数就搞定了。这里的 nonlocal 是关键,它告诉 Python,“我不是新建一个 count,我是用外层的那个变量”。

闭包最让我感叹的一点是,它有点像你生活中的便签纸。你今天写了个 shopping list,贴在冰箱门上,哪怕你人出门了,list 还在,别人来冰箱门前一看,就知道你要买啥。闭包就是那张贴在冰箱门上的便签纸。

再高级点,闭包还是装饰器的底层实现。你不是经常看到 Python 的装饰器 @something 吗?那个其实就是闭包。比如说你要统计一个函数的调用时间:

import time


def timer(func):

    def wrapper(*args, **kwargs):

        start = time.time()

        result = func(*args, **kwargs)

        end = time.time()

        print(f"{func.__name__} took {end - start:.4f}s")

        return result

    return wrapper


@timer

def slow_function():

    time.sleep(1)


slow_function()

这里 wrapper 就是个闭包,它用到了 func,也就是外头 timer 的变量,这样就实现了函数的“功能增强”。不用改函数本身,只在外头加点料,多优雅啊。

但说实话,闭包也有坑。最常见的坑就是“延迟绑定”问题。啥意思呢?看这个代码:

funcs = []

for i in range(5):

    def f():

        return i

    funcs.append(f)


print([f() for f in funcs])  # 你以为会输出 [0,1,2,3,4] 吗?错,是 [4,4,4,4,4]

为啥会这样?因为 f 是个闭包,它引用了外头的 i,而 i 最后变成了 4。所以所有 f() 都返回 4。解决办法也不是没有,比如用默认参数:

funcs = []

for i in range(5):

    def f(x=i):

        return x

    funcs.append(f)


print([f() for f in funcs])  # 这下就对了:[0,1,2,3,4]

这个坑,我真的是踩了不止一次。尤其写 lambda 的时候,那个引用问题非常微妙。lambda 里用外部变量的时候,一定要小心,搞不好就变成了“惊喜”……

说回闭包的优势吧,我觉得它最大优点是“简洁且强大”。你看,传统编程里,如果你要保存某个状态,多半会搞个 class,弄个实例变量。但是在 Python 里,用闭包就可以优雅地搞定,还不会污染命名空间,也不会写一堆 class 的模板代码。

而且闭包还能用于“信息隐藏”。我们写 class 的时候经常讲“封装”,不想让外部访问某个变量,那闭包也能实现一样的效果:

def make_bank_account():

    balance = 0

    def deposit(amount):

        nonlocal balance

        balance += amount

        return balance

    return deposit


account = make_bank_account()

print(account(100))  # 输出 100

print(account(50))   # 输出 150

这里的 balance 是“私有变量”,外面根本访问不到,只能通过 deposit 来改,这不比 class 的封装差。

不过吧,闭包也不是银弹。有时候你一不留神写了个闭包,还带着一堆外部变量,Python 内存就不一定能立马释放。闭包可能会导致循环引用的问题,尤其在复杂结构里嵌套闭包的时候,要特别注意。

还有个使用场景是回调。比如你用 tkinter 写 GUI 或者 asyncio 写异步操作,闭包都特别有用。它让你可以带状态地注册回调函数,听起来就很爽。

我一直觉得,闭包在 Python 里的地位,类似于拉面师傅手里的那碗汤,看起来平平无奇,但真功夫都在里面。你只要开始写点函数式风格的代码,或者涉及多线程、异步、装饰器、回调这些东西,闭包基本是绕不过去的东西。

有时候我也在想,闭包这种写法,说穿了就是“函数 + 环境变量”,那为啥在别的语言里不那么流行?其实 JavaScript 也用得挺多,但在像 Java 或 C++ 这种编译型语言里,闭包就显得重了点,语法也不够优雅。这可能也是为啥 Python 这么受欢迎的一个点吧,语法天然就偏向于让你把函数当一等公民来玩。

当然,闭包也不是哪儿都合适用。比如你的业务逻辑特别复杂,变量多如牛毛,这时候用闭包可能反而不如老老实实写个 class 来得清晰。就像我之前维护一个写了好几个层级嵌套闭包的库,调试起来简直要命,每个函数都带着上下文变量,跳进跳出看的我眼花缭乱。

所以我现在的态度是:闭包是个利器,但用之前想清楚,是不是能让代码更简洁、更有结构,别一不小心写成了晦涩的“黑魔法”。

总之,闭包这玩意儿在 Python 里真的不是装逼用的,而是实打实提高代码质量的工具,特别是你要写一些高阶函数或者是要封装逻辑的时候,它能省你不少事。只不过,写闭包也得像写代码一样,得讲究个度,别用成了自个儿都看不懂的迷宫。

你们平时写代码的时候,会经常用闭包吗?有没有踩过我说的那些坑?

更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

  • 发表于 2025-07-11 09:28
  • 阅读 ( 45 )
  • 分类:Python开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

1335 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 2172 文章
  3. Pack 1335 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章