033-闭包

先来做两个实验,加深一下对外层变量和内层变量的区别和关联:

>>> def funa():
    x = 88
    def funb():
        x = 99
    return print(x)

>>> funa()
88

----------
在这个案例中,虽然内层函数定义了另一个变量x,但最终返回的,仍然是外层函数的变量x。因此x = 88.
>>> def funa():
    x = 88
    def funb():
        x = 99
        print(x)
    return funb()

>>> funa()
99

----------
在这个案例中,print(x)是funb()的一部分,因此最后返回的是funb()中定义的x,因此是99.

上面两个例子都有一个共同点,就是内层函数和外层函数都没有设定参数。如果函数设定了参数,会是怎么样的情况呢?这个等下举例的时候会提到。

闭包:如果一个内层函数调用了他的外层函数的参数。那么我们称这个内层函数和被他调用的参数为一个闭包。

闭包概念示意图

举个例子:

>>> def funa(x):
    def funb(y):
        return x * y
    return funb

>>> a = funa(9)
>>> a(6)
54

从上面例子可以看到,x这个外层函数的参数,在内层函数中被调用。最终返回的是funb,而非funb(),原因是funb这个内层函数是有参数y的,如果返回funb()就相当于缺少了y,没有给y这个参数赋值,最终就会导致报错。

>>> def funa(x):
    def funb(y):
        return x * y
    return funb()

>>> a = funa(8)
Traceback (most recent call last):
  File "", line 1, in 
    a = funa(8)
  File "", line 4, in funa
    return funb()
TypeError: funb() missing 1 required positional argument: 'y'

一开始我完全没有看懂为什么需要输入一个a = funa(8),然后在输入一个a(9),后来明白了。输入funa(8)相当于调用了funa(x),并且将x赋值为8,外层函数内部嵌套了一个内层函数funb(y),运行到这里的时候,就需要你给funb(y)里面的y一个赋值。那么这个赋值要如何赋呢?肯定不能直接funb(9)这样子,因为funb()作为一个内层函数,是不能在全局环境中直接调用的。

而程序已经通过设计解决了这个问题。外层函数返回的结果本身就是funb,此时的funb就等于a(外层函数返回的结果),那么a(9)自然也就相当于funb(9)了。

其实这么写可能更简单一些:

>>> def funa(x):
    def funb(y):
        return x * y
    return funb

>>> funa(8)(9)
72

教科书上没有这么写,这是我通过理解之后推导出来的哦。我可真棒!!


image

正如上一节所说,python并不希望你在函数内部修改全局变量,或者在内层函数中修改外层函数的变量。可如果你一意孤行,那也不是不能改,只是在内层函数中对变量赋值的时候,要做一下全局声明,也就是要加上一个global。

在闭包中也是如此,如果你想在内层函数中对外层函数的变量进行修改,你需要先对变量进行一个声明,即在要修改的变量名前面加上nonlocal。例:

>>> def funa():
    x = 5
    def funb():
        nonlocal x
        x = x + 1
        return x
    return funb

>>> funa()()
6

当然,这是python3中的用法,而在python2中可没有这么智能。我们说外层函数中x=5,内层函数中,x=6,这时候内层函数并不是改变了外层函数的变量值,而是重新生成了一个名称相同但id不同的变量x,并给他赋值为6。为什么要这样操作?因为在这个过程中,x这个变量是放在栈里的。因此要创造一个新的x,来避免将原来的x所覆盖。

可我们有的时候就是要覆盖原来的x,就是要在内层函数中修改外层函数的变量值。那咋办呢?可能你已经想到主意了。我们找一个不放在栈里的东西来表示变量x不就行了吗?

那么那种类型的数据是不放在栈里的呢?序列!

因此,假如我们让外层函数的x = [5],在内层函数中,让x[0] = x[0] + 1这就完美地逃脱了原来规则的束缚。举例如下:

>>> def funa():
    x = [5]
    def funb():
        x[0] = x[0] + 1
        return x[0]
    return funb

>>> funa()()
6

由于python3已经帮我们解决了这个问题了,因此这个投机取巧的办法学习其思想,能看懂,就OK了。

你可能感兴趣的:(033-闭包)