6. Python中的闭包(Closure)

2022/2/21 14:26:45

本文主要是介绍6. Python中的闭包(Closure),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

《Python编程的术与道:Python语言进阶》视频课程
《Python编程的术与道:Python语言进阶》视频课程链接:https://edu.csdn.net/course/detail/28618

闭包 (Closure)

Objects are data with methods attached. Closures are functions with data attached.

A Closure is a function object that remembers values in enclosing scopes even if they are not present in memory.

对象(Object)指的是附带相应方法的数据;闭包(Closure)指的则是附带相应数据的函数。闭包函数能够引用一些并不在当前代码上下文中定义的变量。这些被引用的变量是在闭包函数的包围作用域(enclosing scope)的代码中定义的。这样的一些函数被称为闭包。

让我们一步一步来解释。

首先,嵌套函数是在另一个函数内部定义的函数。 重要的是要注意,嵌套函数可以访问包围作用域的变量。 但是,在python中,它们只是只读的。 但是,可以将nonlocal关键字与这些变量一起显式使用,以便对其进行修改。

def transmit_to_space(message):
    "This is the enclosing function"
    def data_transmitter():
        "The nested function"
        print(message)

    data_transmitter()

print(transmit_to_space("Test message"))
Test message
None

由于上面的例子中data_transmitter函数可以访问message,因此工作的没有问题。 为了演示 nonlocal关键字的使用,考虑下面的例子:

def print_msg(number):
    def printer():
        "Here we are using the nonlocal keyword"
        number=3
        print(number)
    printer()
    print(number)

print_msg(9)
3
9
def print_msg(number):
    def printer():
        "Here we are using the nonlocal keyword"
        nonlocal number
        number=3
        print(number)
    printer()
    print(number)

print_msg(9)
3
3

如果没有nonlocal关键字,则输出将为“ 3 9”,但是使用它,我们得到的结果为“ 3 3”,即修改了number变量的值。

现在,我们返回一个函数对象而不是在其中调用嵌套函数。 (请记住,函数在Python中是对象。)

def transmit_to_space(message):
    "This is the enclosing function"
    def data_transmitter():
        "The nested function"
        print(message)
    return data_transmitter
fun2 = transmit_to_space("Burn the Sun!")
fun2()
Burn the Sun!

即使完成了transmit_to_space()的执行,message还是被保留了下来。 这种即使在其他原始函数结束之后还可将数据附加到某些代码的技术, 被称为闭包。

另一个例子:

def pow_later(x):
    y = 2
    def lazy_pow():
        print('calculate pow({}, {})...'.format(x, y))
        return pow(x, y)
    return lazy_pow

我们调用pow_later pow_later(3),它返回了一个函数对象。

my_pow = pow_later(3)
my_pow
<function __main__.pow_later.<locals>.lazy_pow()>

然后我们调用返回的函数对象。

my_pow()
calculate pow(3, 2)...





9

显然,变量y和参数xpow_later函数的局部变量。 因此,当调用my_pow()时,pow_later函数已经返回,并且其局部变量也消失了。 但是实际上,即使外部作用域pow_later早已消失,my_pow()仍然记住x和y的值。

自由变量(Free variable)

如果函数中的变量既不是局部变量也不是该函数的参数,则此变量称为该函数的自由变量。

简言之,自由变量是在局部使用但在包围作用域内定义的变量。

在我们的例子中,xpow_later的参数,ypow_later的局部变量。 但是在lazy_pow中,xy是自由变量。

my_pow实际上是通过调用pow_later(x)返回的函数对象,是一个闭包

注意,lazy_pow的闭包扩展了lazy_pow函数的作用域,来包括对自由变量x和y的绑定。

在这里插入图片描述

一般来说,闭包是一种将函数与一个环境存储在一起的结构(代码块,函数对象,可调用对象等)。 这里的环境是指有关绑定的自由变量的信息,尤其是自由变量的值或存储位置。

例如,在随后的函数调用之后,将创建一个闭包,将其返回并分配给my_pow

my_pow = pow_later(3)

本质上,此闭包是函数lazy_pow以及自由变量xy在一起的代码。

没有自由变量的函数不是闭包。

def f(x):
    def g():
        pass
    return g

注意,返回的函数g没有自由变量。它的__closure__是None。

什么时候有闭包?

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

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

  • 必须有一个嵌套函数(函数中的函数)
  • 嵌套函数必须引用在包围函数中定义的值 (The nested function must refer to a value defined in the enclosing function.)
  • 包围函数必须返回嵌套函数 (The enclosing function must return the nested function)

何时使用闭包?

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

当在一个类中实现的方法很少(大多数情况下是一个方法)时,闭包可以提供替代的且更优雅的解决方案。 但是,当属性和方法的数量变大时,最好实现一个类。

下面是一个简单的示例,其中闭包可能比定义类和创建对象更可取。

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))
27
# Output: 15
print(times5(3))
15
# Output: 30
print(times5(times3(2)))
30

闭包指延伸了作用域的函数,其中包含在函数定义体中引用,但是不在定义体中定义的非全局变量。它能访问定义体之外定义的非全局变量。



这篇关于6. Python中的闭包(Closure)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程