Python的闭包闭包,说白了就是详解函数里套函数,内层函数记住了外层函数的闭包变量。这玩意儿不复杂,详解但很多人学的闭包时候就是绕不过弯来。
先看个最朴素的详解例子:
def outer(x): def inner(y): return x + y return inner add_5 = outer(5) print(add_5(3)) # 8inner函数用了outer的x,当outer(5)执行完,闭包x=5本该被回收,详解但因为inner还引用着它,闭包这个值就被“记住”了。详解这就是闭包闭包的本质——函数对象携带了定义时的环境。我见过很多初学者把闭包和“返回函数”混为一谈。详解返回函数只是闭包表面动作,真正的详解关键是那个被捕获的变量。你看上面代码,闭包
add_5这个函数,它不只是“一个函数”,它是“一个函数 + 一个绑定好的x=5”。这有点像给函数提前塞了个参数进去,但不用每次调用都传。闭包最常见的坑,是变量绑定时机的问题。看这个:
funcs = [] for i in range(3): def f(): return i funcs.append(f) for func in funcs: print(func()) # 输出 2 2 2,不是 0 1 2很多人第一次看到这个结果都懵了。原因在于
f捕获的i是变量本身,不是值。循环结束后i已经变成了2,所以每个f执行时拿到的都是2。想修正就用默认参数绑定当前值:def f(i=i): return i,或者用functools.partial。闭包在实际代码里最常用的场景,是替代简单的类。比如你要做一个计数器:
def make_counter(): count = 0 def increment(): nonlocal count count += 1 return count return increment c1 = make_counter() c2 = make_counter() print(c1()) # 1 print(c1()) # 2 print(c2()) # 1每个计数器实例都有独立的
count变量,互不干扰。这比写个类要轻量得多。但注意,如果你在increment里直接写count += 1而不声明nonlocal,Python会认为你创建了一个局部变量count,然后报错。这个nonlocal关键字是Python 3才有的,Python 2时代人们用列表绕过去:count = [0],然后修改count[0]。闭包还有个妙用是延迟计算。我写爬虫的时候经常这么干:先定义好请求逻辑,但不立刻执行,等条件满足了再调用。闭包把参数和环境一起封装起来,想什么时候触发就什么时候触发。
不过闭包也不是没有代价。它会让函数对象变大,因为要携带额外的环境信息。如果你在循环里创建成千上万个闭包,内存开销会比普通函数高不少。另外,闭包让代码的调试变困难——你很难直观地看到那个被捕获的变量当前是什么值。我自己的经验是,闭包适合“一对一”的场景,如果逻辑复杂到需要多个方法协作,还是老老实实用类吧。
最后说个冷知识:装饰器本质上就是闭包的应用。你写
@decorator,Python帮你做了func = decorator(func),而decorator返回的那个wrapper函数,捕获了原始func的引用。所以理解了闭包,装饰器就自然理解了——它们不是两个独立的概念,而是同一个机制的不同用法。闭包这东西,你写几次就熟了。关键是记住一句话:内层函数记住的,是外层函数的变量对象本身,不是那个变量的“快照”。把这句话嚼透了,闭包就没什么神秘的了。
顶: 6642踩: 97922






评论专区