Python: 函数式编程与闭包

这是一篇通过代码实例,分析闭包概念的笔记。希望能为大家提供一些参考。

问题

首先看两段Python代码的对比,下图左侧代码输出14,右侧代码输出8
在这里插入图片描述

刚接触函数式编程时不熟悉闭包概念,所以搞不懂为什么输出值会有14。文章后续内容,是我对程序调试过程的记录。

相关概念

  • 函数式编程,意味着函数不仅可以返回数值, 还可以返回需要执行的操作
    Python可以通过def 函数声明或者lambda 表达式,把函数作为参数和返回值。
  • 一段代码在运行时,可能会调用这段代码之外的函数与变量。这些外部函数与变量构成了代码段的上下文环境。
    将构成代码段运行环境的外部内容进行整合,就得到了代码段的一份闭包。 在Python中,可以通过__closure__属性查看。

代码1 调试记录

如上图左侧所示,测试代码1如下:

# test-1
funcs = []
def foo(m):
    for i in range(m):
        def bar(n):
            return n + i
        funcs.append(bar)
foo(10)
print(funcs[3](5))  # 14

在Python中,函数通过值传递方式来接收int类型参数。
但上述代码的bar()函数,没有使用正常的参数传递方式,而是直接在函数内引用外部变量i的值,使得函数foo()bar()产生了内容耦合
因此,每当外部函数foo()更改变量i时,bar()的运算结果就要受影响。
通过调试可以查看到funcs[]内部的函数型变量(function variables)bar(),以及bar()函数的闭包属性__closure__
bar()函数的闭包

闭包属性__closure__内存放了外部变量i的地址。所以,就如上述分析的一样,随着i值的递增,结果的数值也在不断变动:

  • i==3时,funcs[3](5)的结果为 8
    i为3时的结果为8
  • i==9时,funcs[3](5)的结果为 14
    i为9时结果为14

如果在bar()的闭包构建完成后删除变量i,后续调用bar()时就会出错:
删除i后程序报错

代码2 调试记录

测试代码2:

# test-2
funcs = []
def foo(m):
    for i in range(m):
        def bar(n, t=i):
            return n + t
        funcs.append(bar)
foo(10)
print(funcs[3](5))  # 8

把外部变量i转为内部函数的默认参数,使得内容耦合降低至数据耦合。调试时,查看funcs[]的内容可以发现:

  1. 变量i的值被bar()保留到了__defaults__属性中,成为了函数bar()的默认参数;
  2. 由于函数bar()内未直接引用i进行计算,所以__closure__属性内不再保留外部地址。
    funcs[]内部地址

此时,随着i值的递增,结果不再变动。

  • i==3时,funcs[3](5)的结果为 8
    i为3时的结果
  • i==9时,,funcs[3](5)的结果依旧为 8
    i为9时的结果
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值