---
template: overrides/blogs.html
tags:
- python
---
# 理解Python闭包
!!! info
作者:[Vincent](https://github.com/Realvincentyuan),发布于2021-09-21,阅读时间:约6分钟,微信公众号文章链接:[:fontawesome-solid-link:](https://mp.weixin.qq.com/s?__biz=MzI4Mjk3NzgxOQ==&mid=2247484557&idx=1&sn=bd624bdb21757e391d01c5ced51cb5f8&chksm=eb90f7f9dce77eef2a8c67bc0d3637e9d90708ffd061ce4773916fa3c58d137e4b859adf1c40&token=1570026209&lang=zh_CN#rd)
## 1 前言
在使用Python的日常工作中,你或许碰到过类似于这样的代码:
```Python
def make_counter():
# 外层闭包函数
count = 0
def counter():
# 嵌套函数
nonlocal count
count += 1
return count
return counter
```
一个函数里还有一个函数,并且外层函数的返回值是内层的函数,为什么要这样定义函数呢?这样有什么好处,这篇文章我们来揭开它神秘的面纱 - 闭包(Closure)。
## 2 闭包的要点
闭包指延伸了作用域的函数,它会引用一个不在函数定义中的非全局变量(如上述例子中的count),nonlocal的加入能把变量标记为自由变量(Python 3才加入nonlocal关键字),让内层嵌套函数可以修改作用域外的不可变变量。
调用make_counter时,返回一个counter函数对象,每次调用counter时,它会更新count,示例如下:
```Python
# 运行闭包函数
counter = make_counter()
print(counter())
print(counter())
```
输出为:
```python
1
2
```
在这个例子中,有一点需要展开说的是count的历史值的存储位置,count是make_counter函数的局部变量,初始化的时候count的值为0,但调用counter的时候,make_counter函数已经返回了,本地作用域理应不复存在。
在counter函数中,count是自由变量,counter函数实现了对变量count的绑定。可以通过Python中的__code__属性(表示编译后的函数定义体)查看保存的局部变量和自由变量的名称,如下所示:
```Python
# 查看自由变量
counter.__code__.co_freevars
```
输出为:
```
('count',)
```
count的绑定在返回的counter函数的__closure__属性里,其中__closure__的各个元素对应于`counter.__code__.co_freevars`中的一个名称。这些元素是cell对象,可以通过cell_contents属性访问其存储的值,示例如下:
```python
counter.__closure__[0].cell_contents
```
输出为:
```
2
```
闭包能够非常简洁、直观地解决轻量级的问题,如果上述功能用`类`来实现的话,会长成这样:
```Python
# 用类定义一个计数器,从0开始计数
class Counter:
def __init__(self):
self.count = 0
def __call__(self):
self.count += 1
return self.count
counter = Counter()
print(counter())
print(counter())
```
输出为:
```Python
1
2
```
## 3 总结
闭包是一种函数,它保留了定义函数时存在的自由变量的绑定,因此,即便是函数返回后作用域不存在了,那些绑定仍然能够被使用。闭包可以很容易地实现一些简单的类的功能,同时基于此还有很多Python的魔法能够实现,比如装饰器,待下回分解!
希望这次的分享对你有帮助,欢迎在评论区讨论!