美文网首页
Python闭包探幽

Python闭包探幽

作者: Lysias | 来源:发表于2019-08-06 15:41 被阅读0次

    我们想生成一组底数不同的指数函数。

    Edition 1

    def makeFunc():
        return [lambda x: base**x for base in range(5)]
    
    acts = makeFunc()
    
    [act(2) for act in acts]
    >> [16, 16, 16, 16, 16]
    

    base都是4,为什么?我推测,出于效率考虑,python函数并不在定义时“解引用”任何变量。也就是说,将base替换为0, 1...4的操作并没有在定义时发生。而在运行时,base已然为4。


    下面是makeFunc的字节码,上述行为的线索就在其中。

      2           0 LOAD_CONST               1 (<code object <listcomp> at 0x0000018B42A70300, line 2>)
                  2 LOAD_CONST               2 ('makeFunc.<locals>.<listcomp>')
                  4 MAKE_FUNCTION            0
                  6 LOAD_GLOBAL              0 (range)
                  8 LOAD_CONST               3 (5)
                 10 CALL_FUNCTION            1
                 12 GET_ITER
                 14 CALL_FUNCTION            1
                 16 RETURN_VALUE
    
    Disassembly of <code object <listcomp> at 0x0000018B42A70300, line 2>:
      2           0 BUILD_LIST               0
                  2 LOAD_FAST                0 (.0)
            >>    4 FOR_ITER                16 (to 22)
                  6 STORE_DEREF              0 (base)
                  8 LOAD_CLOSURE             0 (base)
                 10 BUILD_TUPLE              1
                 12 LOAD_CONST               0 (<code object <lambda> at 0x0000018B42A70270, line 2>)
                 14 LOAD_CONST               1 ('makeFunc.<locals>.<listcomp>.<lambda>')
                 16 MAKE_FUNCTION            8
                 18 LIST_APPEND              2
                 20 JUMP_ABSOLUTE            4
            >>   22 RETURN_VALUE
    
    Disassembly of <code object <lambda> at 0x0000018B42A70270, line 2>:
      2           0 LOAD_DEREF               0 (base)
                  2 LOAD_FAST                0 (x)
                  4 BINARY_POWER
                  6 RETURN_VALUE
    

    注意观察第三段字节码,即<code object <lambda>>部分。这段字节码显示出lambda函数的运行过程:

    • 解引用外部变量(base)
    • 加载本地变量(x)
    • 二元幂运算
    • 返回。

    这说明,lambda函数被调用时,名称base的解引用才会发生。如果base在函数定义时就被替换,这里的字节码应该是加载常数才对。

    以上讨论让我们对python变量解引用的时机有了如下认识:

    python变量在执行时解引用


    Edition 2

    def makeAction(base):
        return lambda x: base**x
    
    acts = [makeAction(i) for i in range(5)]
    
    [act(2) for act in acts]
    >> [0, 1, 4, 9, 16]
    

    为每个lambda制作一个独立的闭包,顺利做出了5个不一样的函数。

    相关文章

      网友评论

          本文标题:Python闭包探幽

          本文链接:https://www.haomeiwen.com/subject/ocaidctx.html