闭包浅析

首先:python

若是在一个函数的内部定义了另外一个函数,外部的咱们叫他外函数,内部的咱们叫他内函数。编程

而后:闭包

咱们来看看闭包的官方定义: 在一个外函数中定义了一个内函数内函数里运用了外函数的临时变量而且外函数的返回值是内函数的引用。这样就构成了一个闭包。app

分析:编程语言

通常状况下,在咱们认知当中,若是一个函数执行完毕,函数的内部(即名称空间)全部东西都会释放掉,被回收还给内存,局部变量也会消失。函数

可是闭包是一种特殊状况,若是外函数在结束的时候发现其名称空间(做用域)中的临时变量未来会在内部函数中使用到,就把这个临时变量绑定给了内部函数,而后本身再结束。spa

举例说明:3d

# outer是外部函数 a和b都是外函数的临时变量
def outer( a ):
b = 8
# inner是内函数 def inner(): #在内函数中 用到了外函数的临时变量a和b print(a+b) # 外函数的返回值是对内函数的引用 return inner if __name__ == '__main__':    demo = outer(6) #调用外函数传入参数6 #此时外函数两个临时变量 a是6 b是8 ,outer外函数调用执行,其返回值就是对内函数的引用,将引用的内函数inner的内存地址赋值存给demo # 外函数结束的时候发现内部函数将会用到(由于demo只是引用了inner的内存地址,只有它加括号才能执行内部代码,因此这里说是将会用到)本身的临时变量,这两个临时变量就不会释放,会绑定给这个内部函数inner# 咱们调用内部函数,看一看内部函数是否是能使用外部函数的临时变量    demo() # 14 # demo存了外函数的返回值,也就是inner函数的引用,这里至关于执行inner函数 demo2 = outer(9) demo2() #17

 若是要更全面的理解闭包就须要知道一些知识:code

① 外函数的返回值是内函数的引用

  python中一切都是对象,包括变量、函数,其实都是对象。对象

  当咱们进行a=1的时候,其实是在内存当中开辟一片空间存放了值1,而后用a这个变量名存了1所在内存位置的引用(内存地址)。

  a只不过是一个变量名字,a里面存的是1这个数值所在的内存地址,就是a里面存了数值1的引用。

  相同的道理,在python中定义一个函数def demo(): 的时候,会在内存中开辟一块空间,用于存放该函数的代码、内部的局部变量等等。

  这个demo只不过是一个变量名字,它里面存了这个函数所在位置的引用而已。

  咱们还能够进行x = demo, y = demo, 这样的操做就至关于,把demo里存的东西赋值给x和y,这样x 和y 都指向了demo函数所在的引用,在这以后咱们能够用x() 或者 y() 来调用咱们本身建立的demo() ,调用的实际上根本就是一个函数,x、y和demo三个变量名存了同一个函数的引用。

 

  有了上面对引用的解释,就能够更深入的理解“返回内函数的引用”这句话了。

  对于闭包,在外函数outer中最后return了inner,咱们在调用外函数 demo = outer() 的时候,outer返回了inner,inner是一个函数的引用,这个引用被存入了demo中。因此接下来咱们再进行demo() 的时候,至关于运行了inner函数。

  同时咱们知道,函数名后+括号就至关于调用执行了这个函数,若是不+括号,至关于只是一个函数的名字,里面存了函数所在位置的引用。

② 闭包中内函数修改外函数局部变量:

  在闭包内函数中,咱们能够随意使用外函数绑定来的临时变量,可是正常事不能修改的,由于做用域不同,没法修改

  在基本的python语法当中,一个函数能够随意读取全局数据,可是要修改全局数据的时候有两种方法:一、global 声明全局变量 二、全局变量是可变类型数据的时候能够修改

  在闭包内函数也是相似的状况,在内函数中想修改闭包变量(外函数绑定给内函数的局部变量)的时候:

  一、在python3中,能够用nonlocal 关键字声明 一个变量, 表示这个变量不是局部变量空间的变量,须要向上一层变量空间找这个变量。

  二、在python2中,没有nonlocal这个关键字,咱们能够把闭包变量改为可变类型数据进行修改,好比列表。

举个栗子:

#修改闭包变量的实例 # outer是外部函数 a和b都是外函数的临时变量
def outer( a ): b = 10  # a和b都是闭包变量
    c = [a] #这里对应修改闭包变量的方法2
    # inner是内函数
    def inner(): #内函数中想修改闭包变量
        # 方法1 nonlocal关键字声明
 nonlocal b b+=1
        # 方法二,把闭包变量修改为可变数据类型 好比列表
        c[0] += 1
        print(c[0]) print(b) # 外函数的返回值是内函数的引用
    return inner if __name__ == '__main__': demo = outer(5) demo() # 6 11

还有一点须要特别注意:

  使用闭包的过程当中,一旦外函数被调用一次返回了内函数的引用,虽然每次调用内函数,是开启一个函数执行事后消亡,可是闭包变量实际上只有一份,每次开启内函数都在使用同一份闭包变量  

  

def outer(x): def inner(y): nonlocal x x += y return x return inner a = outer(10) print(a(1))  # 11
print(a(3))  # 14(注意,这里不是13,由于第二次使用的依然是x这个变量,只不过他在第一次引用的是第一次的结果11了)

两次分别打印出11和14,因而可知,每次调用inner的时候,使用的闭包变量x其实是同一个。

总结

闭包的做用:

   一、装饰器

   二、面向对象:经历了上面的分析,咱们发现外函数的临时变量送给了内函数。这其实跟类对象的状况同样,对象有好多相似的属性和方法,因此咱们建立类,用类建立出来的对象都具备相同的属性方法。闭包也是实现面向对象的方法之一。在python当中虽然咱们不这样用,在其余编程语言入好比avaScript中,常常用闭包来实现面向对象编程

   三、实现单例, 其实这也是装饰器的应用。

 

def singleton(cls):
    _instance = cls('127.0.0.1', 3306)

    def wrapper(*args, **kwargs):
        if len(args) != 0 or len(kwargs) != 0:
            obj = cls(*args, **kwargs)
            return obj
        return _instance

    return wrapper


@singleton  # MySQL=singleton(MySQL) #MySQL=wrapper
class MySQL:
    def __init__(self, ip, port):
        self.ip = ip
        self.port = port

 

   四、本身还没想到的其它...,欢迎补充^-^

相关文章
相关标签/搜索