python高级语法--闭包篇

来源:互联网 发布:n5230软件下载 编辑:程序博客网 时间:2024/06/11 15:21
        闭包不是python特有的语法特征,在 javascript等一些动态语言中也存在闭包的概念,初学者对闭包的理解往往只停留在语法层面,本文将从闭包的定义,应用场景,执行过程等多方面来阐述python中的闭包。
1. 闭包的定义
如果在函数内部再定义一个函数,并且这个函数用到了外部函数的一些变量,我们就将这个内部函数和用到的变量统称为闭包。从概念中,我们只能在脑海里对闭包到底长啥样有个大致的印象,下面这个图实现了一个简单的闭包:
上面这段代码的输出结果是5,我们抛开语法层面的执行过程,站在python解释器的角度去分析这个程序的执行过程。python解释器首先会对整个程序扫描一遍,这个过程是记录一些特殊的语法现象,比如一个函数是闭包函数,就要特殊标记一下。执行完一遍初步的扫描之后,解释器就会从第一行代码开始解释执行,最开始遇见def这个关键字会跳过函数中的具体代码,因为这只是定义不是调用,所以不会执行。解释器真正开始解释执行的第一行代码是 f = Outter(2),,我们知道在python中万物皆对象,由于python解释器之前在扫描过程中发现Outter隐含着一个闭包,此时就会在内存中创建一个对象,这个对象的内存图如下所示:
python解释器会在内存中创建一个名为Outter的对象,这个对象的一个属性就是out_num,wrapper是这个对象的一个方法,(当然Outter内部还会有 Python解释器自动添加的一些其它属性,比如__name__等)。下面敲黑板,注意Outter函数返回值是内部函数Wrapper,也就说f接收的返回值是内部函数Wrapper的引用,此时f==Wrapper,接下来执行的res=f(3)实际上就是f= Wrapper(3), Wrapper这个函数可以使用之前赋值的old_num这个变量,我们将这种语法现象称之为闭包。其实本质就是一个对象中的函数使用了之前对象中赋过值的一个属性,仅此而已.
2. 闭包的应用场景
我们知道平面中表示一条直线可以用y=a*x+b进行表示,下面我们要实现的一个功能是根据给定的输入a,b,x三个值求出对应的y的值。第一种实现方法:
def  calc(a,b,x):     return a*x + bcalc(1,1,2)calc(1,13)
我们发现上面这种写法,每次都需要传入a和b的值,但是同一条直线上a和b的值是固定不变的,求同一条直线上的y值时每次传入a和b实际上是一个重复的操作,这时候我们可以采用闭包的方式,将固定不变或者变化较少的变量放在外部函数中,将频繁变化的变量放在内部函数中:
第二种实现方法:
def  calc(a,b):    def inner(x): return a*x+b   return inner
3. 修改闭包作用域内的变量
在python2和python3中,在内部函数中修改外部函数用到的外部变量的方式是不同的。我们知道如果在一个函数中对全局变量进行修改要用global修饰符,否则不能在函数中修改全局变量,但是这种情况只是针对不可变类型,对于List这种可变类型,我们仍然可以在函数中对全局的List类型的变量进行修改。在闭包中,内部函数用到的外部函数的变量既不属于全局作用域,也不属于局部作用域,它属于介于两者之间的闭包作用域,在python3中可以使用nonlocal关键字对其进行修改。在python2中由于没有引入nonlocal关键字,对闭包作用域的变量进行修改时,可以先将其转化成list中的一个元素,然后通过对可变类型list的修改达到修改变量的目的(这种操作在python2和python3中都可以使用)。
在python3中修改的方法:
在python2和python3中都通用的方法:

4. 闭包总结
1. 闭包实际上是类变量的一种优化,它可以对数据进行记忆,在程序中如果实现某些依赖重复数据的功能时,闭包既避免了我们利用普通函数实现时需要每次都传入重复数据的冗余(函数不具备记忆性),又避免了对于一个简单功能的实现为了记录数据而去单独创建一个类(我们可以把闭包理解为对象的轻量版)
2. 闭包也是有缺陷的,由于内部函数引用了外部函数的局部变量(也就是闭包空间内的变量),这个变量不会及时释放,这样会消耗内存。
原创粉丝点击