zoukankan      html  css  js  c++  java
  • 闭包学习-Python 篇

    作为一个没有女朋友的码农,情人节这天只能敲敲代码,或者写写一些自己学习上的一些心得

    下面我就说一说我学习“闭包” 这个结构时的一些问题和心得吧!

    刚接触闭包的时候,感觉闭包很简单啊,在Python 中,不就是一个函数内再定义一个函数么,里面的函数用了外面函数的变量,这有什么难的?可随着不断的深入学习和研究,才发现了闭包结构的魅力。

    首先学习闭包之前,你必须有一些Python 基础的了解,最重要的一点,就是函数名的概念,不了解的同学看下面这个例子:

    def test1():
        print("in test1")
    
    test1()          # in test1
    f = test1
    f()              # in test1

           分析一下,首先我们定义了一个函数 test1, 函数的作用就是打印一句话“in test1”,接着我们调用了一下这个函数,发现没有什么问题,能正常打印。

           接下来的就比较奇怪了,我们把test1 这个函数名赋值给了一个变量f (赋值这个词用的不是很恰当),然后对f 进行了函数调用,发现能打印出和调用test1 一样的内容,是不是很奇怪,我们并没有定义f 这个函数。对比以下小栗子,可能你就明白其中的原因了。

    a = 10
    b = a
    print(b)          # 10
    

           第一行我们先创建了变量a,并把数值10 赋值给它,也就是数值10 的引用加一, 然后第二行是把变量a 赋值给了变量b,Python 中的赋值就是引用的传递 ,也就是将变量a 引用的内容 传递给了变量b,此时数值10 的引用再次加一,所以会有了第三行的结果,我们并没有直接把数值10 赋值给变量b,而是通过变量a 的引用传递,使得变量b 也被赋值了10.

            说了那么多,大家有没有发现什么问题?没错,函数名test1 不就相当于 变量a 吗? f 不就相当于变量b 吗?不同的是 test1 引用的是一个函数代码空间,而a 引用的是一个整数10,那么是不是就可以说函数和变量名一样,可以把它引用的代码空间传递给其他变量名? 当然是这样的,如果不成立的话,可能就不会有类似闭包这个结构了。

    接下来,我们再根据下面的例子,来继续学习闭包

    def outer(number_out):
        def inner(number_in):
            return number_out + number_in
        return inner
    
    f1 = outer(100)
    f2 = outer(100)
    print(f1)          # <function outer.<locals>.inner at 0x000002102F5E02F0>
    print(id(f1))          # 2268537422576
    print(id(f2))          # 2268537422440
    

      分析f1 = outer(100) ,这个100 其实就是number_out 的实参,并且调用返回了 inner 这个函数名,结果为 f1 = inner,通过引用传递,将内部定义的函数 inner所引用的函数代码块,传递给了f1,此时f1 是一个 函数对象,但为什么使用相同函数调用,传入的实参也一样,两个对象却不一样?这是因为f1 = inner 的这个结果,是只有在调用outer(number_out) 的时候才去定义的inner 这个函数,作用域不同,也就不会是相同的inner 了,这也就是闭包的魅力之一了,此时调用f1(number_in) 和调用f2(numberi_in) 之间互不影响。 

    如果觉得上面的例子还算简单,那么再看一下稍微复杂一点的例子:

    def line_conf(a, b):
        def line(x):
            return a * x + b
        return line
    
    line1 = line_conf(1, 2)
    line2 = line_conf(3, 3)
    print(line1(3))          # 5
    print(line2(3))          # 12
    

      上面这个闭包的例子,函数line 和 变量a,b 构成闭包,通过确定line_conf 的参数a,b,转换成实际的函数就是,line1 :y = x + 2,line2:y = 3x + 3,所以我们只需要变换参数a,b 的值,就能得到不同的一次函数表达式,如果没有闭包结构,每次都要传递a, b, x 三个参数,减少了代码的可移植性,由此可以看出,闭包结构具有提高代码复用性的作用

    注意:由于闭包引用了外层函数的局部变量,则外层函数的局部变量没有及时释放,消耗内存

    总结:

          闭包结构就是一个嵌套定义的函数,在外层函数开始的时候才开始定义内层函数的定义,然后将内层函数代码块的引用传递给函数外的对象

          内层函数和使用外层函数的提供的变量所构成的整体就被称为闭包

    扩展:闭包结构中,修改外层函数局部变量的方法

    Python 2

    def counter(start=0):
        count = [start]
        def inner():
            count[0] += 1
            return count[0]
        return inner
    
    
    f1 = counter()
    f2 = counter()
    
    print(f1())          # 1
    print(f1())          # 2
    print(f2())          # 1
    print(f2())          # 2
    

      由此也可以看出,f1 和f2 确实是互不影响的两个对象.

    Python 3 

    def counter(start=0):
        def inner():
            nonlocal start
            start += 1
            return start 
        return inner
    
    f1 = counter()
    f2 = counter()
    
    print(f1())          # 1
    print(f1())          # 2
    print(f2())          # 1
    print(f2())          # 2
    

      

  • 相关阅读:
    iptables之NAT端口转发设置
    Windows server 2008 R2远程桌面3389端口号修改
    Nginx启动错误:error while loading shared libraries: libpcre.so.1
    sp_change_users_login 'Update_One', '用户名', '登录名';
    Sqlserver 数据库定时自动备份
    ES DSL 基础查询语法学习笔记
    kafka命令使用
    kafka集群中常见错误的解决方法:kafka.common.KafkaException: Should not set log end offset on partition
    快速排序法(双冒泡排序法)
    运算符
  • 原文地址:https://www.cnblogs.com/yungiu/p/10374046.html
Copyright © 2011-2022 走看看