python中的闭包(closure)

背景

本文尝试介绍Python中的闭包(closure),包括闭包是什么? 为什么要使用闭包?如何使用闭包?

嵌套函数及非局部变量

在介绍闭包之前,需要先明白什么是嵌套函数和非局部变量。在一个函数(fun1)中定义的另一个函数(fun2)称为嵌套函数。嵌套函数(fun1)可以访问外围作用域的变量,即为非局部变量。换一句话说,嵌套函数能够访问enclosing scope(闭包作用域,或者外围作用域,或者外层作用域)下的变量。这些非局部变量默认情况下是只读的,为了修改它们,必须显式地将它们声明为非局部变量(使用nonlocal关键字)。具体示例如下:

    def print_msg(msg):
        # 这是外层函数

        def printer():
            # 这是嵌套函数
            print(msg)

        printer()

    # 调用外层函数
    print_msg("Hello World")

输出:

Hello World

可以看出,嵌套函数printer()是可以访问到非局部变量msg,即外层函数中的msg

定义一个闭包函数

如果上述示例print_msg()函数的最后一行不是调用printer(),而是返回printer呢?具体代码改成如下:

    def print_msg(msg):

        def printer():
            # 这是一个嵌套函数
            print(msg)

        return printer  # 返回嵌套函数

    # Now let's try calling this function.
    # 输出: Hello World
    another = print_msg("Hello World")
    another()

可以看出输出结果是:

Hello World

这里调用print_msg()函数的时候传入了字符串Hello World,返回函数名为another。在调用another()时,之前传入的字符串Hello World信息仍然保留,尽管我们已经完成了print_msg()函数的执行。

这种将一些数据(比如上述示例中的Hello World)附加到代码中的技术在Python中称为闭包。enclosing scope中的会被存储,即使该变量超出作用域,或者函数本身被从当前命名空间中删除,也会记住外围作用域中的这个值。比如:

del print_msg
another()

依然能够输出:

Hello World

此时尝试调用:

print_msg("Hello")

报错如下:

File "test.py", line 306, in <module>
    print_msg("Hello World")
NameError: name 'print_msg' is not defined

因为返回函数仍然可以正常工作,而原始的函数已经被删除,无法像之前那样起作用。

什么是闭包

正如上面的例子所示,当嵌套函数在其外围作用域中引用一个值时,就出现了一个闭包。在Python中创建闭包,须满足的条件总结如下:

  • 必须有一个嵌套函数(函数中有函数)
  • 嵌套函数必须引用外围函数中定义的值
  • 外围函数必须返回嵌套函数

什么时候使用闭包

闭包可以避免使用全局变量,并提供某种形式的数据隐藏。它还可以为这个问题提供一个面向对象的解决方案。当类中需要实现的方法很少(大多数情况下只有一个方法)时,闭包可以提供一种替代的、更优雅的解决方案。但是,当属性和方法的数量比较多时,最好实现一个类。

下面是一个简单的例子,闭包可能比定义类和创建对象更合适。

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)

# 输出: 27
print(times3(9))

# 输出: 15
print(times5(3))

# 输出: 30
print(times5(times3(2)))

另外,需要注意的是:闭包函数中包含的值是可以找到的。所有的函数对象都有__closure__属性。当是闭包函数时,该属性返回cell objects的元组。以上述示例进行说明,其中times3times5都是闭包函数,打印输出:

print(make_multiplier_of.__closure__)
print(times3.__closure__)

输出结果如下:

None
(<cell at 0x7f5c8d8cb618: int object at 0xa68b00>,)

cell对象中的属性cell_contents存储着闭包值:

print(times3.__closure__[0].cell_contents)
print(times5.__closure__[0].cell_contents)

输出结果如下:

3
5
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值