Python
语言是支持函数式编程的,我们可以在一个函数的函数体中定义另一个完整的函数,甚至返回这个函数.在函数内部定义的函数和外部定义的函数是相同的,唯一的区别就是在函数内部定义的函数是不能被外部访问的.
下面的一段代码定义了两个函数f()
和 g()
,在函数f()
中把g()
作为返回值返回,这里的g()
你可以参考C语言
中函数的指针来进行理解,它们两个几乎在语义和用法上几乎完全相同(C语言中函数名即为指向函数的指针
).
|
|
我们把函数g()
的定义移到f()
内部,这样除了f()
之外的区域就无法调用g()
,代码如下:
|
|
但是有一种情况我们是无法把函数g()
移动到函数f()
外面的,请观察一下如下代码:
|
|
list是一个列表,作为参数传进函数f()
中,此时我们发现无法把函数g()
移到函数f()
的外面,因为函数g()
的功能是计算list中所有元素值的和,必须依赖于函数f()
的参数;如果函数g()
不在函数f()
里面,则无法取得数据进行计算,这样我们就引出了闭包的概念.
闭包(Closure):
|
|
接下来就要重点介绍本文标题中提到的那个不易察觉的陷阱了.
观察如下代码:
|
|
这里的count()
函数是通过函数的闭包来返回一个包含三个函数的列表,分别是计算1x1的函数, 计算2x2的函数, 计算3x3的函数,那么我们分别调用执行f1()
函数, f2()
函数, f3()
函数的执行结果是多少呢?会不会是我们期望的1 4 9 呢?您不妨自己在机器上运行一下这段代码,如果嫌麻烦的话可以看一下我的运行结果截图:
竟然是三个9!!! 第一次遇到这个问题其实我是拒绝滴,难道是加了特效么,我duang的一下就迷惑了
翻了翻书才知道这是Python语言的特性所致.当count()
函数返回三个函数时,这三个变量所引用的变量i的值已经变成了3,因为在返回的时候三个函数并没有被调用,所以此时它们并没有及时计算它们对应的i乘以i的值,等到三个函数都返回时,然后调用三个函数,此时i的值已经为3,计算i乘以i的值自然就都是9了.
所以在返回闭包的情况下,我们一定要注意的一点就是:
返回函数千万不要引用任何一个循环变量,或者在之后会发生改变的变量.
当然对于这种情况我们还是有解决方法的.我们在内层函数f()
内再定义一个内层函数g()
,用这个函数的参数绑定循环变量的当前值,这样的话,无论循环变量之后如何改变,每一次循环中的循环变量i的值就都保存在了第三层函数g()
中,如此得到了我们期望的输出结果.
|
|
程序的执行结果如下:
如果嫌这段代码过于臃肿,可以考虑使用lambda表达式进行缩减.