zoukankan      html  css  js  c++  java
  • Python3(九) 闭包

    一. 一切皆对象

    函数式编程并没有标准定义,如果代码非常繁琐则考虑使用。

    学习闭包的概念,不是python独有的。

    其他大多数语言中的函数只是一段可执行的代码,并不是对象。

    python中的函数是对象,一切皆对象。可以把函数赋值给变量:

    a = 1

    a = '2'

    a = def

    甚至可以把函数当作另外一个函数的参数传递或者当成返回值返回,而C#中要封装成委托。

    二.什么是闭包:闭包=函数+函数定义时的环境变量

    我们尝试从概念上去理解一下闭包。

    在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。—— 维基百科

    用比较容易懂的人话说,就是当某个函数被当成对象返回时,夹带了外部变量,就形成了一个闭包。

    1.

    code1

    def curve_pre():
     
        def curve():
     
            pass
     
    curve()    #找不到,因为curve()的作用域仅限于curve_pre()的内部

    code2

    def curve_pre():
     
        def curve():
            print('This is a function')
     
            pass
     
        return curve    #函数可以作为结果返回
     
    f = curve_pre()    #函数可以赋值
     
    f()#执行函数,不会报错
    #This is a function

    code3

    def curve_pre():
     
        a = 25         #局部变量在curve的外部
     
        def curve(x):  #接受抛物线的x值
     
            return a * x * x
     
        return curve    #返回一个函数
     
     
    f = curve_pre()
     
    print(f(2))         #调用curve()函数
     
    #100

    code4

    a = 10
     
    def f1(x):
     
        return a * x * x
     
    print(f1(2))
     
    #40         #局部变量找不到会去上一级找

    code 3 和 code4 对比

    def curve_pre():
     
        a = 25 #局部变量在curve的外部
     
        def curve(x): #接受抛物线的x值
     
            return a * x * x
     
        return curve #返回一个函数
     
     
     
    a = 10     #定义a = 10
     
    f = curve_pre()
     
    print(f(2)) #调用curve()函数
     
    #100        #仍然是a = 25的取值,取得是定义时的环境变量,这就是闭包

    函数及其外部环境变量所构成的整体叫做闭包

    2.环境变量要在函数外部,但不能是全局变量:

    a = 25    #a定义为了全局变量
     
    def curve_pre():
     
        def curve(x): #接受抛物线的x值
     
            return a * x * x
     
        return curve #返回一个函数
     
     
     
    a = 10
     
    f = curve_pre()
     
    print(f(2)) #调用curve()函数
     
    #40    #a的值被改变了,因为没有形成闭包

    查看:

    def curve_pre():
     
        a = 25 #局部变量在curve的外部
     
        def curve(x): #接受抛物线的x值
     
            return a * x * x
     
        return curve #返回一个函数
     
     
     
    a = 10
     
    f = curve_pre()
     
    print(f.__closure__)
     
    print(f.__closure__[0].cell_contents)#取出闭包的环境变量
     
    print(f(2)) #调用curve()函数
     
     
    #(<cell at 0x0031AAF0: int object at 0x0FF93A80>,)
     
    #25    #这里是a的值
     
    #100

    三.一个事例看看闭包 

    1.闭包的意义:闭包保存的是一个环境,把函数现场保存起来了。

                  闭包 = 函数 + 函数定义时的环境变量(不能是全局变量)

    2. 闭包的经典误区      从外层向内层分析

    def f1():
     
        a = 10
     
        def f2():
     
            a = 20    #对a重新赋值局部变量,不会影响到外部
     
            print(a)
     
        print(a)    #10
     
        f2()    #20 #f1内部调用f2
     
        print(a)    #10
     
    f1()
     
    #10
     
    #20
     
    #10

    验证其是不是一个闭包:

    1

    def f1():
     
        a = 10
     
        def f2():
     
            a = 20
     
            # print(a)
     
        #print(a)
     
        f2()
     
        #print(a)
     
     
     
    f = f1()    #f1是None类型
     
    print(f.__closure__)    #报错

    (2)加上返回值,仍然不是闭包函数:

    def f1():
     
        a = 10
     
        def f2():
     
            a = 20    #a被认为是一个局部变量了,就不认为是个环境变量了
     
            return a
     
        return f2
     
     
     
    f = f1()
     
    print(f.__closure__)    #没有__closure__属性
     
    #None

    (3)去掉f2()中的赋值后是闭包函数:

    def f1():
     
        a = 10
     
        def f2():
     
            #a = 20    #删除a = 20,此时a被python认为是一个局部变量
     
            return a
     
        return f2
     
     
     
    f = f1()
     
    print(f.__closure__)
     
    #(<cell at 0x02F5AAF0: int object at 0x0FF93990>,)

    原因:环境变量不能当作一个变量去赋值,而是一定要去引用外部。

    四.出个题,用闭包解决!

    闭包不是必不可少的东西,只是可以使你的代码架构更加合理。

    题目:

    旅行者,x = 0 为起点,没走一步加1,计算出旅行者当前所处的位置。

    关键点:每次调用时需要调用上次的结果

    1.先用非闭包解决一下

    origin = 0
     
     
    def go(step):
     
        global origin    #将origin变成全局变量
     
        new_pos = origin + step
     
        origin = new_pos  #等号左边的origin被python认识是局部变量
     
        return origin
     
     
     
    print(go(2))
     
    print(go(3))
     
    print(go(6))
     
    #2
     
    #5
     
    #11

    2.再用闭包解决一下

    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(tourist(6))
     
    #2
    #0
     
    #5
     
    #11

    并没有改变全局变量origin的值

    五.小谈函数式编程

    • python和Javascript都会使用闭包。
    • 闭包特点:在一个函数外部间接调用函数内部的变量,从模块级别间接调用局部变量。
    • 极易造成内存泄漏。

    闭包的概念

  • 相关阅读:
    【JAVA】空指针异常
    【JAVA】分隔字符串的小技巧
    【JAVA】imageio异常:不支持图像类型
    权限设计“终极”解决方案
    一直困扰我的.net 2.0和,net 1.1同时运行的问题解决了
    乱谈成本三
    SandBar学习笔记
    不是吧!怎么这样
    学习Java的相关知识
    代码自动生成工具的补充
  • 原文地址:https://www.cnblogs.com/wlgaojin/p/12292356.html
Copyright © 2011-2022 走看看