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 "<pyshell#17>", line 1, in <module>
    a = funa(8)
  File "<pyshell#16>", 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了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,875评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,569评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,475评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,459评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,537评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,563评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,580评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,326评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,773评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,086评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,252评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,921评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,566评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,190评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,435评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,129评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,125评论 2 352

推荐阅读更多精彩内容