美文网首页
Python学习(十五)--闭包的用法

Python学习(十五)--闭包的用法

作者: 白面葫芦娃92 | 来源:发表于2020-08-10 11:28 被阅读0次

    1.一切皆对象

    python支持函数式编程,但不一定非要用函数式编程
    其他语言里,函数只是一段可执行的代码,并不是对象
    python里,一切皆对象,简化了学习难度
    例如:

    def f1():
      pass
    a = f1()
    print(type(f1))
    ==>
    <class 'function'>
    

    在其他语言中把一个函数赋值给一个变量是做不到的;
    python可以将一个函数作为另一个函数的参数,传递到另外的函数里,也可以把一个函数当做另外一个函数的返回结果

    2.闭包是什么

    (1)

    def curve_pre():
        def curve():
            pass
    
    curve()
    ==>
    Traceback (most recent call last):
      File ".\c14.py", line 5, in <module>
        curve()
    NameError: name 'curve' is not defined
    #直接调用会报错函数未定义
    

    (2)

    def curve_pre():
        def curve():
            print('This is a funciton')
        return curve
    
    curve_pre()
    print(type(curve_pre()))
    ==>
    <class 'function'>
    #直接调用curve_pre,看不到返回值,其实curve_pre返回了一个函数即curve,
    #因此想要看到This is a funciton,需要先把curve赋值给一个变量,再通过这个变量,对curve进行调用,如下:
    
    def curve_pre():
        def curve():
            print('This is a funciton')
        return curve
    f = curve_pre()
    f()
    ==>
    This is a funciton
    

    (3)

    def curve_pre():
        a = 25
        def curve(x):
            return a*x*x
        return curve    
    f = curve_pre()
    print(f(2))
    ==>
    100
    

    上面的例子很容易理解,但如果,在调用curve前,再给a重新赋值,a=10,最终结果会等于100还是40?

    def curve_pre():
        a = 25
        def curve(x):
            return a*x*x
        return curve
    a = 10
    f = curve_pre()
    print(f(2))
    ==>
    100
    

    最终结果等于100,而不是40
    这就体现了闭包的作用,不会被外部变量赋值影响;闭包=函数+环境变量
    环境变量一定要在函数的外部,但是还不能是全局变量(这是一个很重要的特点)

    print(f.__closure__)
    print(f.__closure__[0].cell_contents)
    ==>
    #上方的return curve语句,返回的不仅仅是def curve,同时把环境变量a=25也返回了
    (<cell at 0x00000273ED0A6D68: int object at 0x00007FFF1BAC9640>,)
    25
    

    (4)看看下边这个例子能不能理解

    def f1():
        a = 10
        def f2():
            a = 20      
            print(a)    #a此时被python认为是一个局部变量,不会影响到外部的变量
        print(a)
        f2()
        print(a)
    
    f1()
    ==>
    10    
    20
    10
    

    3.闭包怎么用

    假设一个人在x轴上行走,初始位置x=0,第一次走了2步,result=2;第二次走了3步,result=5;第三次走了6步,result=11,这个需求怎么实现?
    (1)非闭包方式1

    origin = 0
    def go(step):
        new_pos = origin + step
        origin = new_pos
        return origin
    print(go(2))
    print(go(3))
    print(go(6))
    ==>
    Traceback (most recent call last):
      File ".\c14.py", line 37, in <module>
        print(go(2))
      File ".\c14.py", line 34, in go
        new_pos = origin + step
    UnboundLocalError: local variable 'origin' referenced before assignment
    #因为origin在=前出现了,python就认为origin是一个局部变量,而执行到第4行的时候,origin并没有被定义,因此报错
    

    (2)

    origin = 0
    def go(step):
        new_pos = origin + step
        return new_pos
    print(go(2))
    print(go(3))
    print(go(6))
    ==>
    2
    3
    6
    # 并没有输出想要的结果,每次调用origin都为0
    

    (3)

    origin = 0
    def go(step):
        global origin
        new_pos = origin + step
        origin = new_pos
        return new_pos
    print(go(2))
    print(go(3))
    print(go(6))
    ==>
    2
    5
    11
    # 实现了功能,但是有一个弊端,全局变量origin,很容易在另一个函数或者过程中被赋上新值,这样go的返回值就可能发生改变,下面来看用闭包如何实现
    

    (4)

    origin = 0
    def factory(pos):
        def go(step):
            new_pos = pos + step
            pos = new_pos
            return new_pos
        return go
    
    tourist = factory(origin)
    print(tourist(2))
    print(tourist(3))
    print(tourist(5))
    ==>
    Traceback (most recent call last):
      File ".\c14.py", line 41, in <module>
        print(tourist(2))
      File ".\c14.py", line 35, in go
        new_pos = pos + step
    UnboundLocalError: local variable 'pos' referenced before assignment
    # 报错:局部变量没有被定义
    

    (5)

    origin = 0
    def factory(pos):
        def go(step):
            nonlocal pos
            new_pos = pos + step
            pos = new_pos
            return new_pos
        return go
    
    tourist = factory(origin)
    print(tourist(2))
    print(tourist(3))
    print(tourist(6))
    ==>
    2
    5
    11
    

    得到了想要的结果,验证一下每一步全局变量origin有没有被改变

    origin = 0
    def factory(pos):
        def go(step):
            nonlocal pos
            new_pos = pos + step
            pos = new_pos
            return new_pos
        return go
    
    tourist = factory(origin)
    print(tourist(2))
    print(origin)
    print(tourist(3))
    print(origin)
    print(tourist(6))
    print(origin)
    ==>
    2
    0
    5
    0
    11
    0    #全局变量origin一直没有被改变
    

    再来看看函数tourist的环境变量是不是一直在变化

    origin = 0
    def factory(pos):
        def go(step):
            nonlocal pos
            new_pos = pos + step
            pos = new_pos
            return new_pos
        return go
    
    tourist = factory(origin)
    print(tourist(2))
    print(tourist.__closure__[0].cell_contents)
    print(tourist(3))
    print(tourist.__closure__[0].cell_contents)
    print(tourist(6))
    print(tourist.__closure__[0].cell_contents)
    ==>
    2
    2
    5
    5
    11
    11   
    

    环境变量的状态得到了保存,因此能得到想要的结果
    这个环境变量是要常驻内存的,使用时要注意;
    除了使用闭包,也可以用面向对象的办法,用类变量来保存环境变量的中间状态

    相关文章

      网友评论

          本文标题:Python学习(十五)--闭包的用法

          本文链接:https://www.haomeiwen.com/subject/btbzrktx.html