大家好,我是kelly,今天给大家分享Python闭包。
一、认识闭包
1、感性上认识
先运行下面的这段代码
从上图可知,Python闭包是一个嵌套函数,这个嵌套函数定义在一个外部函数内部,可以访问外部函数的变量。
闭包一旦创建,外部函数的变量会绑定在闭包上,即使外部函数运行结束后,外部函数的变量依然存在,闭包始终可以访问(甚至修改)。
2、定义上认识
维基百科对闭包的描述:
“1、在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是在支持头等函数的编程语言中实现词法绑定的一种技术。闭包在实现上是一个结构体,它存储了一个函数(通常是其入口地址)和一个关联的环境(相当于一个符号查找表)。
2、闭包跟函数最大的不同在于,当捕捉闭包的时候,它的自由变量会在捕捉时被确定,这样即便脱离了捕捉时的上下文,它也能照常运行。
3、闭包可以用来在一个函数与一组“私有”变量之间建立关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。变量的作用域仅限于包含它们的函数,因此无法从其它程序代码部分进行访问。不过,变量的生存期是可以很长,在一次函数调用期间所建立所生成的值在下次函数调用时仍然存在。正因为这一特点,闭包可以用来完成信息隐藏,并进而应用于需要状态表达的某些编程范型中。”
专业点的描述:闭包,英文名词closure,是一个函数以及其捆绑的周边环境状态的引用组合。
因此,闭包和普通函数的区别在于,
(1)闭包捆绑了外部函数的变量
(2)拓展了外部函数的变量的作用域,将外部函数的变量缓存在内存中,其生存期不再受到外部函数的限制。
二、闭包怎么使用
1、闭包怎么使用
举例:以机器学习中的线性回归为例,已知2个参数的权重和截距,求输入具体参数值时的回归值。
这个案例展示了外部函数如何给闭包提供了属性,对于这种用法,实际上通过Python类也能实现。
等价的Python类实现逻辑为:
可见,使用Python类方式的代码量增加了,逻辑更加清晰。此外,如果存在大量属性,建议优先使用Python类,逻辑更清晰。
进一步说明:对于闭包的外层函数中的变量,会一直缓存在内存中,直到程序运行结束。
2、在闭包内怎么修改外部函数变量的值
直接修改是否可行?
很明显,在闭包中直接修改外部变量变量行不通,原因是:
(1)Python变量的作用域优先级为:当前局部作用域变量>外层作用域变量>当前模块中的全局变量>python内置变量。
(2)+=是一个赋值运算,计算结果赋值给inner_func局部作用域的x_base;而在inner_func内部,x_base实际上还不存在,执行+=运算时又无法引用外部函数的x_base。
怎么解决?
使用nonlocal关键字声明,声明闭包中所引用的实际为外部函数中的变量。
说明:与nonlocal关键字对应的是global关键字,global关键字用于声明局部作用域内的变量实际为全局变量,声明后可以读取和修改全局变量。
三、闭包的使用场景、注意点
优点:
(1)避免使用全局变量带来的污染
(2)变量隐藏,对闭包的外层函数的变量进行缓存(通过闭包的__closurre__属性取值可知),对外层函数之外的作用域不可见。
注意点:
(1)闭包所引用外层函数变量带来的内存泄露、内存消耗等问题
四、判断函数是不是闭包
1、根据闭包的定义,人工判断的依据是2点:
(1)是不是嵌套函数(内部函数)
(2)有没有访问外部函数的变量
2、判断函数的__closure__属性,判断取值是否包含ceil对象。
变量a指向inner_func函数,由于outer_func有2个参数:wieght和intercept,因此,inner_func函数作为闭包,属性__closure__包含2个元组,元组cell对象的cell_content属性分别取值100和10。
本文原创,原始版本发表链接:
kelly会在公众号「kelly学技术」不定期更新文章,感兴趣的朋友可以关注一下,期待与您交流。
--over--