零基础学习 Python 之闭包

写在以前

在正式讲「闭包」以前咱们首先得先知道「嵌套函数」这么一个东西,我在以前的文章中(零基础学习 Python 之函数对象)说过,函数不仅仅能够做为对象来传递,还能够在一个函数里面嵌套一个函数,这个就是咱们今天要讲的嵌套函数。编程

嵌套函数

首先咱们来看一个例子:bash

>>> def my_name():
...    def your_name():
...            print('your_name() is two dog')
...    print('my_name() is rocky')
...
复制代码

上面就是一个简单的嵌套函数的例子,在上面的代码中,在函数 my_name() 中定义了函数 your_name(),而 your_name() 就称为 my_name() 的「内嵌函数」,由于它是在 my_name() 里面的定义。闭包

而后咱们来调用 my_name(),会获得下面的结果:编程语言

>>> my_name()
my_name() is rocky
复制代码

这个结果说明在上面的调用方式和内嵌函数的写法中,your_name() 这个函数根本没被调用,或者咱们能够这么说,那就是 my_name() 没有按照从上到下的顺序依次执行其里面的代码。函数式编程

那么我想要 your_name() 这个内嵌函数也执行,该怎么作呢?其实在 my_name() 里面显示的调用一下 your_name() 函数就行了,请看下面的代码:函数

>>> def my_name():
...    def your_name():
...            print('your_name() is two dog')
...    your_name() #显示的调用内嵌函数
...    print('my_name() is rocky')
...
复制代码

咱们如今来调用 my_name(),运行结果以下:学习

>>> my_name()
your_name() is two dog
my_name() is rocky
复制代码

如今咱们再来思考一个问题,咱们能不能在 my_name() 外面单独的调用其内嵌函数 your_name() 呢?咱们来试一下:ui

>> your_name()
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
NameError: name 'your_name' is not defined
复制代码

结果会显示错误信息,这说明这样调用是不行的,缘由就是 your_name() 是定义在 my_name() 里面的函数,它生效的范围仅限于 my_name() 函数体以内,也就是说它的做用域就是 my_name() 的范围而已,既然是这样,那么 your_name 在使用变量的时候也就会收到 my_name() 的约束。spa

咱们再来看一个例子:.net

>>> def fun1():
...    a = 1
...    def fun2():
...            a += 1
...            print('fun2 -- a = ',a)
...    fun2()
...    print('fun1 -- a = ',a)
...
复制代码

在看下面的结果以前,请你想想这个函数的结果会是什么?加入你思考完毕,请看下面的结果:

>>> fun1()
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "<stdin>", line 6, in fun1
 File "<stdin>", line 4, in fun2
UnboundLocalError: local variable 'a' referenced before assignment
复制代码

你猜对了么?结果是运行错误!咱们观察报错的信息,缘由是 fun2() 里面使用了 fun1() 的变量 a,按照表达式, Python 解释器认为这个变量应该在 fun2() 中创建,而不是引用 fun1() 中的变量,因此才报错。

在 Python 中,咱们可使用 nonlocal 这个关键词,具体操做见下例:

>>> def fun1():
...    a = 1
...    def fun2():
...   nonlocal a
...            a += 1
...            print('fun2 -- a = ',a)
...    fun2()
...    print('fun1 -- a = ',a)
...
复制代码

而后咱们调用 fun1() 函数,获得以下结果:

fun2 -- a = 2
fun1 -- a = 2
复制代码

综上所述就是嵌套函数的原理,剩下的就是在实践中去运用它,达到加深理解的目的。

这个嵌套函数,其实能够制做动态的函数对象,而这个话题延伸下去,就是所谓的「闭包」。

闭包

咱们都知道在数学中有闭包的概念,但此处我要说的闭包是计算机编程语言中的概念,它被普遍的使用于函数式编程。

关于闭包的概念,官方的定义颇为严格,也很难理解,在《Python语言及其应用》一书中关于闭包的解释我以为比较好 -- 闭包是一个能够由另外一个函数动态生成的函数,而且能够改变和存储函数外建立的变量的值。乍一看,好像仍是比较很难懂,下面我用一个简单的例子来解释一下:

>>> a = 1
>>> def fun():
...     print(a)
...
>>> fun()
1
>>> def fun1():
...     b = 1
...
>>> print(b)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined
复制代码

毋庸置疑,第一段程序是能够运行成功的,a = 1 定义的变量在函数里能够被调用,可是反过来,第二段程序则出现了报错。

在函数 fun() 里能够直接使用外面的 a = 1,可是在函数 fun1() 外面不能使用它里面所定义的 b = 1,若是咱们根据做用域的关系来解释,是没有什么异议的,可是若是在某种特殊状况下,咱们必需要在函数外面使用函数里面的变量,该怎么办呢?

咱们先来看下面的例子:

>>> def fun():
...    a = 1
...    def fun1():
...            return a
...    return fun1
...
>>> f = fun()
>>> print(f())
1
复制代码

若是你仔细看过上面文章的内容,你必定以为的很眼熟,上述的本质就是咱们所讲的嵌套函数。

在函数 fun() 里面,有 a = 1 和 函数 fun1() ,它们两个都在函数 fun() 的环境里面,可是它们两个是互不干扰的,因此 a 相对于 fun1() 来讲是自由变量,而且在函数 fun1() 中应用了这个自由变量 -- 这个 fun1() 就是咱们所定义的闭包。

闭包实际上就是一个函数,可是这个函数要具备

  • 1.定义在另一个函数里面(嵌套函数);
  • 2.引用其所在环境的自由变量。

上述例子经过闭包在 fun() 执行完毕时,a = 1依然能够在 f() 中,即 fun1() 函数中存在,并无被收回,因此 print(f()) 才获得告终果。

当咱们在某些时候须要对事务作更高层次的抽象,用闭包会至关舒服。好比咱们要写一个二元一次函数,若是不使用闭包的话相信你能够垂手可得的写出来,下面让咱们来用闭包的方式完成这个一元二次方程:

>>> def fun(a,b,c):
...    def para(x):
...            return a*x**2 + b*x + c
...    return para
...
>>> f = fun(1,2,3)
>>> print(f(2))
11
复制代码

上面的函数中,f = fun(1,2,3) 定义了一个一元二次函数的函数对象,x^2 + 2x + 3,若是要计算 x = 2 ,该一元二次函数的值,只须要计算 f(2) 便可,这种写法是否是看起来更简洁一些?

当咱们在后面学习了类的知识之后,再回过头来看闭包的应用,其实你会有更深的认识,这个咱们在后面再作讨论,先知道有类这么一个概念就行了。

写在以后

固然闭包在实际的应用中还有不少方面,做为零基础入门这个系列咱们就到此为止,不作深究,可能在后面我会在别的系列中再进一步的讲一下,若是你如今对这个方面很感兴趣,能够 Google 一下这方面的文章,有不少的。

更多内容,欢迎关注公众号「Python空间」,期待和你的交流。

在这里插入图片描述
相关文章
相关标签/搜索