• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
李晓否
一个不应该编程的程序员
博客园    首页    新随笔    联系   管理    订阅  订阅
13、闭包

Python语言是支持函数式编程的,我们可以在一个函数的函数体中定义另一个完整的函数,甚至返回这个函数.在函数内部定义的函数和外部定义的函数是相同的,唯一的区别就是在函数内部定义的函数是不能被外部访问的.

 

下面的一段代码定义了两个函数f() 和 g(),在函数f()中把g()作为返回值返回,这里的g你可以参考C语言中函数的指针来进行理解,它们两个几乎在语义和用法上几乎完全相同(C语言中函数名即为指向函数的指针).

 

def g():  
    print 'g()...'  
  
def f():  
    print 'f()...'  
    return g  

 

我们把函数g()的定义移到f()内部,这样除了f()之外的区域就无法调用g(),代码如下:

 

 

def f():  
    print 'f()...'  
    def g():  
        print 'g()...'  
    return g  

 

但是有一种情况我们是无法把函数g()移动到函数f()外面的,请观察一下如下代码:

 

list = [1, 2, 3, 4, 5]  
  
def f(list):  
    def g():  
        return sum(list)  
    return g  

 

list是一个列表,作为参数传进函数f()中,此时我们发现无法把函数g()移到函数f()的外面,因为函数g()的功能是计算list中所有元素值的和,必须依赖于函数f()的参数;如果函数g()不在函数f()里面,则无法取得数据进行计算,这样我们就引出了闭包的概念.

 

 

闭包(Closure):内层函数引用了外层函数的变量(包括它的参数),然后返回内层函数的情况,这就是闭包.

 

接下来就要重点介绍本文标题中提到的那个不易察觉的陷阱了.

 

观察如下代码:

 

# 希望一次返回3个函数,分别计算1x1,2x2,3x3:  
def count():  
    fs = []  
    for i in range(1, 4):  
        def f():  
             return i*i  
        fs.append(f)  
    return fs  
  
f1, f2, f3 = count()  
  
print f1(), f2(), f3()  

 

这里的count()函数是通过函数的闭包来返回一个包含三个函数的列表,分别是计算1x1的函数, 计算2x2的函数, 计算3x3的函数,那么我们分别调用执行f1()函数, f2()函数, f3()函数的执行结果是多少呢?会不会是我们期望的1 4 9 呢?您不妨自己在机器上运行一下这段代码,如果嫌麻烦的话可以看一下我的运行结果截图:

 

(在这里跟大家安利一下我用的IDE,伟大的Jet BRAINS公司[1]的推出的PyCharm,功能强大,有收费版和免费社区版[2],我用的就是社区版)

 

竟然是三个9!!! 第一次遇到这个问题其实我是拒绝滴,难道是加了特效么,我duang的一下就迷惑了疑问疑问疑问

 

翻了翻书才知道这是Python语言的特性所致.当count()函数返回三个函数时,这三个变量所引用的变量i的值已经变成了3,因为在返回的时候三个函数并没有被调用,所以此时它们并没有及时计算它们对应的i乘以i的值,等到三个函数都返回时,然后调用三个函数,此时i的值已经为3,计算i乘以i的值自然就都是9了.

 

所以在返回闭包的情况下,我们一定要注意的一点就是:

返回函数千万不要引用任何一个循环变量,或者在之后会发生改变的变量.

 

当然对于这种情况我们还是有解决方法的.我们在内层函数f()内再定义一个内层函数g(),用这个函数的参数绑定循环变量的当前值,这样的话,无论循环变量之后如何改变,每一次循环中的循环变量i的值就都保存在了第三层函数g()中,如此得到了我们期望的输出结果.

 

代码如下:

 

 

#coding:utf-8  
__author__ = 'chad'  
# 希望一次返回3个函数,分别计算1x1,2x2,3x3:  
def count():  
    fs = []  
    for i in range(1, 4):  
        def f(j):  
            def g():  
                return j*j  
            return g  
        fs.append(f(i))  
    return fs  
  
f1, f2, f3 = count()  
  
print f1(), f2(), f3()  

 

程序的执行结果如下:

 

如果您嫌这段代码过于臃肿,可以考虑使用lambda表达式进行缩减.

感谢大家.

 

posted on 2018-01-11 18:18  李晓否  阅读(118)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3