Pythonのクロージャは、詳細な

まず、閉鎖機能のために必要な条件:

クロージャ関数は関数オブジェクトを返す必要があります

関数によって返される関数クロージャは(グローバル変数にはできません一般的に)外部変数を参照する必要があり、内部関数が戻るには、返す必要はありません

閉鎖のいくつかの典型的な例:

ENV >>>パイソン3.6 
    NO.1の
    デフline_conf(B):
         デフ線(x)は:
             返す * X + B
         戻りライン
    
    #1 NO.2の
    デフline_confを():
        A = 1 
        、B = 2 デフライン(X):
             プリント(*はX + b)は
         戻りラインNO.3のデフ_line_(B):
         DEF line_c(C):
             デフ線(X):
                 返す *を(X ** 2)+ B * X + C
            
 
        
 
    
    リターンラインの
         リターン line_c

           プリント文が4行のコードが含まれていない、書かれたクロージャは、6行ですああビット間違って見えますか?それを必要なコードの量よりも多くの閉鎖を達成するためにどのように?私は今の需要を持って、心配しないでください。

            直線的に再定義100!

            だから今より少ないコードは誰ですか?明らかに、これは簡単な計算で、クロージャの方法により、通常の練習は、2つのアプローチの違い定義100本の直線、符号量を必要なコードの2行を追加する間だけ、1行のコードを追加する必要がある行を追加:100 + 6 - (100 * 2 + 4)= -98。コードの実際の量の線の数百数十までの環境で定義された単一機能は、今回の閉鎖の役割は大幅にコードの再利用性を向上させること、はい、解明し始めたことに留意すべきです!

            注:外部変数参照パラメータの閉鎖機能は、必ずしも親の関数ではない、親は、コードの第2段落で、このような「ウォームアップ」と、範囲内の任意の変数の関数であってもよいです。

 

     第二に、どのように明示的に「閉鎖」を参照してくださいするには

接上面的代码块:

    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

 

おすすめ

転載: www.cnblogs.com/111testing/p/11128875.html