zoukankan      html  css  js  c++  java
  • Python闭包详解

    首先给出闭包函数的必要条件:

    闭包函数必须返回一个函数对象

    闭包函数返回的那个函数必须引用外部变量(一般不能是全局变量),而返回的那个函数内部不一定要return

    几个典型的闭包例子:

    # ENV>>> Python 3.6
        # NO.1
        def line_conf(a, b):
            def line(x):
                return a * x + b
            return line
        
        # NO.2
        def line_conf():
            a = 1
            b = 2
     
            def line(x):
                print(a * x + b)
            return line
     
        # NO.3
        def _line_(a,b):
            def line_c(c):
                def line(x):
                    return a*(x**2)+b*x+c
                return line
            return line_c

               不包括print语句的代码是4行,闭包写法是6行,看起来有点不对劲啊?怎么闭包实现需要的代码量还多呢?别急,我现在有个需求:

                再定义100条直线!

                那么现在谁的代码量更少呢?很明显这个是可以简单计算出来的,采用闭包的方式添加一条直线只需要加一行代码,而普通做法需要添两行代码,定义100条直线两种做法的代码量差为:100+6 -(100*2+4) = -98。需要注意的是,实际环境中定义的单个函数的代码量多达几十上百行,这时候闭包的作用就显现出来了,没错,大大提高了代码的可复用性!

                注意:闭包函数引用的外部变量不一定就是其父函数的参数,也可以是父函数作用域内的任意变量,如“热身”中的第二段代码:

         二、如何显式地查看“闭包”

    接上面的代码块:
    
        L = line_conf()
        print(line_conf().__closure__) #(<cell at 0x05BE3530: int object at 0x1DA2D1D0>,
        # <cell at 0x05C4DDD0: int object at 0x1DA2D1E0>)
        for i in line_conf().__closure__: #打印引用的外部变量值
            print(i.cell_contents) #1  ; #2
                __closure__属性返回的是一个元组对象,包含了闭包引用的外部变量。
    
    若主函数内的闭包不引用外部变量,就不存在闭包,主函数的_closure__属性永远为None:
    
        def line_conf():
            a = 1
            b = 2
            def line(x):
                print(x+1)  #<<<------
            return line
        L = line_conf()
        print(line_conf().__closure__) # None
        for i in line_conf().__closure__: #抛出异常
            print(i.cell_contents)

     三、为何叫闭包?

     

                 如你所见,line_A对象作为line_conf返回的闭包对象,它引用了line_conf下的变量b=1,在print时,全局作用域下定义了新的b变量指向20,最终结果仍然引用的line_conf内的b。这是因为,闭包作为对象被返回时,它的引用变量就已经确定(已经保存在它的__closure__属性中),不会再被修改。

                   是的,闭包在被返回时,它的所有变量就已经固定,形成了一个封闭的对象,这个对象包含了其引用的所有外部、内部变量和表达式。当然,闭包的参数例外。

        四、闭包可以保存运行环境

                   思考下面的代码会输出什么?

     关于这个问题的深入探讨(python新手理解起来可能需要点时间),我们先看下面的代码(2019/5/19增):

    _list = []
    for i in range(3):
        def func():
            return i+1
        func.__doc__ = i
        func.__hash__ = i
        func.__repr__ = i
        func.__defaults__ = tuple([i]) #这个属性必须是tuple类型
        func.__name__ = f'{i}'
        func.hello = i  #自定义一个属性并赋值
        # 不能再玩了
        _list.append(func)
     
    for f in _list:
        print(f.__doc__,
              f.__hash__,
              f.__repr__,
              f.__defaults__,
              f.__name__,
              f.hello,
              f(),
              )
    # 输出
    # 0 0 0 (0,) 0 0 3
    # 1 1 1 (1,) 1 1 3
    # 2 2 2 (2,) 2 2 3

      代码中我在保存函数时,修改了函数的一些属性(前几个叫做magic method,是函数对象默认拥有的),使它们等于循环内的变量i,hello属性显然是我自定义的一个属性,也让它等于了i。

        然后,我们循环打印每个函数的这些属性,可以发现,咦~  这些属性居然可以保存这个变量i     :)

        嗯,是的,函数的一些基本属性在定义时就会有一个初始的确定值(不论这个值是由可变或不可变对象构成,都是一个完整拷贝,不受源变量变动影响); 闭包保存这个变量的原理是一样的,它用的是函数的__closure__属性,这个属性还有一点特殊,它是只读的,不能由人为修改。(function还有一个__code__属性,这个对象很牛)

        这部分内容是对闭包和函数对象的更深一层的探讨,理解后更上一层楼;

        不过当你不知道这些属性时是做什么用时,最好不要修改它们。

    五、闭包的实际应用

                    现在你已经逐渐领悟“闭包”了,趁热打铁,再来一个小例子:

                  看到这里,你也可以试着自己写出一个简单的闭包函数。

                    OK,现在来看一个真正在实际环境中会用到的案例:

                    1、【闭包实现快速给不同项目记录日志】

      import logging
        def log_header(logger_name):
            logging.basicConfig(level=logging.DEBUG, format='%(asctime)s [%(name)s] %(levelname)s  %(message)s',
                                datefmt='%Y-%m-%d %H:%M:%S')
            logger = logging.getLogger(logger_name)
     
            def _logging(something,level):
                if level == 'debug':
                    logger.debug(something)
                elif level == 'warning':
                    logger.warning(something)
                elif level == 'error':
                    logger.error(something)
                else:
                    raise Exception("I dont know what you want to do?" )
            return _logging
     
        project_1_logging = log_header('project_1')
     
        project_2_logging = log_header('project_2')
     
        def project_1():
     
            #do something
            project_1_logging('this is a debug info','debug')
            #do something
            project_1_logging('this is a warning info','warning')
            # do something
            project_1_logging('this is a error info','error')
     
        def project_2():
     
            # do something
            project_2_logging('this is a debug info','debug')
            # do something
            project_2_logging('this is a warning info','warning')
            # do something
            project_2_logging('this is a critical info','error')
     
        project_1()
        project_2()

     原文地址https://blog.csdn.net/sc_lilei/article/details/80464645

  • 相关阅读:
    JavaScript的正则表达式的基础
    运用JS判断代码可以参考学习
    调用百度地图代码
    运用了css,js
    中国地图(Highmaps)
    Centos
    代理模式【 动态代理与静态代理】
    java集合 collection-list-LinkedList 模拟一个堆栈或者队列数据结构。
    java集合 collection-list-LinkedList
    java集合 collection-list-vector
  • 原文地址:https://www.cnblogs.com/111testing/p/11128875.html
Copyright © 2011-2022 走看看