python 闭包_Python傻傻分不清楚的闭包

477144e06e2e627fad57d02269828be8.png 闭包和装饰器作为Python技术进阶迈不过的坎,不知道让多少人多掉了好几根头发。这两个知识点常常会在一些项目源码中出现,但是自己用起来却很容易出错。本来想把Python中的闭包和装饰器一次性讲完的,但是这么一来篇幅太长了,而且闭包这部分内容也需要一定时间来消化,因此装饰器就下期再讲吧。 那么什么是闭包呢?维基百科中给出的定义是: 8add8396c10557def9d790814391fc15.png 啥...头等函数?自由变量?百科都不讲人话的... 头等函数是什么呢?就是函数可以作为参数、返回值,这在万物皆为对象的Python中当然是支持的。 自由变量是什么呢?这就不得不提一下函数作用域,Python文档中对局部变量、全局变量,和自由变量的定义是这样的:
If a name is bound in a block, it is a local variable of that block, unless declared as nonlocal. If a name is bound at the module level, it is a global variable. (The variables of the module code block are local and global.) If a variable is used in a code block but not defined there, it is a free variable.
直观一点:
variable_1 = 1def outer():    variable_2 = 2    def inner():        variable_3 = 3        return variable_1 + variable_2 + variable_3    return inner
输出:
6
在上例中,对于inner函数来说,variable_1 是全局变量,variable_2 是自由变量,variable_3 是局部变量。 由于自由变量的特殊性,既不能是定义在module level的全局变量,又不能是定义在函数内部的局部变量,因此自由变量出现的场景就是嵌套函数之中。因而闭包也是出现在满足一定条件的嵌套函数中,也就是对应维基百科中的“它存储了一个函数和一个关联的环境”,和“它既要包括约束变量,也要包括自由变量,有些函数也可能没有自由变量”。 然后说道,“闭包跟函数最大的不同在于,当捕捉闭包的时候,它的自由变量会在捕捉时被确定”,那么怎么捕捉自由变量呢? 在Python的所有函数对象,都拥有__closure__属性,如果这个函数是一个闭包的话,那么它返回的是一个由cell对象组成的元组对象。cell对象的cell_contents属性就是闭包中的自由变量。 依旧以上一个例子为基础,输出test的__closure__:
print(test.__closure__)print(type(test.__closure__))print(test.__closure__[0].cell_contents)print(type(test.__closure__[0]))
输出:
(0x0000008FB06EA678: int <class 'tuple'>2<class 'cell'>
简而言之,自由变量被存储起来了,也就是所谓的实现了“捕捉”,因而说是“脱离了捕捉时的上下文,它也能照常运行”。由于是Python自己处理的,因而不需要写代码的人去担心,我们所需要思考的就是写出正确的闭包。 但是看到这里,还是不知道啥是闭包呀...就知道是要嵌套函数,用自由变量...那么这个例子是闭包吗?
def outer_1():    v1 = 1    def inner_1():        v2 = 2        return v1 + v2    return inner_1()test_1 = outer_1()print(test_1)
当然不是啦,因为在这个例子中,第9行的时候outer_1()函数执行,而outer_1()函数返回的是inner_1()的计算结果,也就是说inner_1()函数执行完了。这个时候inner_1()函数释放了对v1的引用,这么一来v1的引用计数就是0,v1被清除,并没有被捕捉,也不能脱离原函数而存在。 那么再看一个例子,这个是闭包吗?
def outer_2():    v1 = 1    def inner_2():        v2 = 2        return v1 + v2    return inner_2test = outer_2()print(test())
当然是闭包啦。因为在这个例子中,outer_2()返回的是inner_2,是一个函数对象,不是inner_2的计算结果。作为一个对象,有自己的内存空间,而__closure__属性保存了自由变量v1,因而v1不会在outer_2()执行完之后被清除。 总结一下,Python中的闭包要满足以下条件: 1、需要有一个内嵌函数,也就是上面例子中的inner_2函数。 2、内嵌函数需要引用定义在外部函数中的变量,也就是内嵌函数要用到自由变量。 3、内嵌函数需要被返回,即外部函数的返回结果是内部函数对象,不是内部函数的计算结果。这一点是最关键的一点,是区别普通嵌套函数与闭包的本质。思考题:下例的输出结果会是什么?
def outer_3():    result = []    for i in range(3):        def inner_3():            return i*i        result.append(inner_3)    return resulttrap0, trap1, trap2 = outer_3()print(trap0())print(trap1())print(trap2())
请思考一下再往下看~输出:
444
很奇怪,为什么不是0、1、2呢?
这个原因我就不写了,如果你知道为什么,就在评论区评论吧~END

b5cac82d2296014ae6c0da71a974a509.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值