# Python 闭包

> 原文： [https://www.programiz.com/python-programming/closure](https://www.programiz.com/python-programming/closure)

#### 在本教程中，您将了解 Python 闭包，如何定义闭包以及使用它的原因。

## 嵌套函数中的非局部变量

在了解什么是闭包之前，我们必须首先了解什么是嵌套函数和非局部变量。

在另一个函数内部定义的函数称为嵌套函数。 嵌套函数可以访问定义范围的变量。

在 Python 中，默认情况下，这些非局部变量是只读的，我们必须将它们明确声明为非局部变量（使用[`nonlocal`关键字](/python-programming/keyword-list#nonlocal)）才能进行修改。

以下是访问非局部变量的嵌套函数的示例。

```py
def print_msg(msg):
    # This is the outer enclosing function

    def printer():
        # This is the nested function
        print(msg)

    printer()

# We execute the function
# Output: Hello
print_msg("Hello")
```

**输出**

```py
Hello
```

我们可以看到嵌套`printer()`函数能够访问外层函数的非本地`msg`变量。

* * *

## 定义闭包函数

在上面的示例中，如果函数`print_msg()`的最后一行返回了`printer()`函数而不是调用它，将会发生什么情况？ 这意味着该函数的定义如下：

```py
def print_msg(msg):
    # This is the outer enclosing function

    def printer():
        # This is the nested function
        print(msg)

    return printer  # returns the nested function

# Now let's try calling this function.
# Output: Hello
another = print_msg("Hello")
another()
```

**输出**：

```py
Hello
```

这很不寻常。

用字符串`"Hello"`调用`print_msg()`函数，并将返回的函数绑定到名称`another`上。 调用`another()`时，尽管我们已经完成了`print_msg()`函数的执行，但仍会记住该消息。

这种将某些数据（在这种情况下为`"Hello`）附加到代码的技术在 Python 中称为**闭包**。

即使变量超出范围或函数本身已从当前名称空间中删除，也将记住定义范围中的该值。

尝试在 Python Shell 中运行以下命令以查看输出。

```py
>>> del print_msg
>>> another()
Hello
>>> print_msg("Hello")
Traceback (most recent call last):
...
NameError: name 'print_msg' is not defined
```

在这里，即使删除原始函数，返回的函数仍然可以使用。

* * *

## 什么时候拥有闭包？

从上面的示例可以看出，当嵌套函数在其定义范围内引用一个值时，在 Python 中会有一个闭包。

以下几点总结了在 Python 中创建闭包必须满足的条件。

*   我们必须有一个嵌套函数（函数在函数内部）。
*   嵌套函数必须引用在外层函数中定义的值。
*   外层函数必须返回嵌套函数。

* * *

## 什么时候使用闭包？

那么，闭包有什么好处呢？

闭包可以避免使用全局值，并提供某种形式的数据隐藏。 它还可以为该问题提供面向对象的解决方案。

当在一个类中实现的方法很少（大多数情况下是一个方法）时，闭包可以提供另一种更优雅的解决方案。 但是，当属性和方法的数量变大时，最好实现一个类。

这是一个简单的示例，其中闭包可能比定义类和创建对象更可取。 但是，偏好是您的全部。

```py
def make_multiplier_of(n):
    def multiplier(x):
        return x * n
    return multiplier

# Multiplier of 3
times3 = make_multiplier_of(3)

# Multiplier of 5
times5 = make_multiplier_of(5)

# Output: 27
print(times3(9))

# Output: 15
print(times5(3))

# Output: 30
print(times5(times3(2)))
```

**输出**：

```py
27
15
30
```

[Python 装饰器](/python-programming/decorator)也广泛使用了闭包。

最后，最好指出可以发现外层函数中包含的值。

所有函数对象都具有`__closure__`属性，如果它是闭包函数，则该属性返回单元格对象的元组。 参考上面的示例，我们知道`times3`和`times5`是闭包函数。

```py
>>> make_multiplier_of.__closure__
>>> times3.__closure__
(<cell at 0x0000000002D155B8: int object at 0x000000001E39B6E0>,)
```

单元格对象具有存储关闭值的属性`cell_contents`。

```py
>>> times3.__closure__[0].cell_contents
3
>>> times5.__closure__[0].cell_contents
5
```