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都会使用闭包。
    • 闭包特点:在一个函数外部间接调用函数内部的变量,从模块级别间接调用局部变量。
    • 极易造成内存泄漏。

    闭包的概念

  • 相关阅读:
    2. Add Two Numbers
    1. Two Sum
    22. Generate Parentheses (backTracking)
    21. Merge Two Sorted Lists
    20. Valid Parentheses (Stack)
    19. Remove Nth Node From End of List
    18. 4Sum (通用算法 nSum)
    17. Letter Combinations of a Phone Number (backtracking)
    LeetCode SQL: Combine Two Tables
    LeetCode SQL:Employees Earning More Than Their Managers
  • 原文地址:https://www.cnblogs.com/wlgaojin/p/12292356.html
Copyright © 2011-2022 走看看