pythonにおける closure と自由変数

ほほう、上位の関数(maker)のローカル変数は、 下位の関数(func)から見るとグローバルに見えるのかと思ったが、 ローカル変数はどこまでいってもローカルなんじゃな。

ちょっと違う。func_code を見るとか dis モジュールを使うともう少し詳細が見える。

import dis

g = 0

def a():
    x = 0
    def inner():
        return x * 2
    return inner

def b():
    def inner():
        x = 10
        return x * 2
    return inner
    
def c():
    def inner():
        global g
        return g * 2
    return inner

def inspect(func):
    sep = '-' * 60
    print '=' * 60
    print func.func_name.center(60)
    print '=' * 60

    names = 'co_varnames co_cellvars co_freevars'.split()

    for name in names:
        print 'outer', name, '=', getattr(func.func_code, name)

    for name in names:
        print 'inner', name, '=', getattr(func().func_code, name)

    print sep
    print 'inner code'
    print sep
    dis.dis(func())

if __name__ == '__main__':
    inspect(a)
    inspect(b)
    inspect(c)

結果

============================================================
                             a
============================================================
outer co_varnames = ('inner',)
outer co_cellvars = ('x',)
outer co_freevars = ()
inner co_varnames = ()
inner co_cellvars = ()
inner co_freevars = ('x',)
------------------------------------------------------------
inner code
------------------------------------------------------------
  9           0 LOAD_DEREF               0 (x)
              3 LOAD_CONST               1 (2)
              6 BINARY_MULTIPLY
              7 RETURN_VALUE
============================================================
                             b
============================================================
outer co_varnames = ('inner',)
outer co_cellvars = ()
outer co_freevars = ()
inner co_varnames = ('x',)
inner co_cellvars = ()
inner co_freevars = ()
------------------------------------------------------------
inner code
------------------------------------------------------------
 14           0 LOAD_CONST               1 (10)
              3 STORE_FAST               0 (x)

 15           6 LOAD_FAST                0 (x)
              9 LOAD_CONST               2 (2)
             12 BINARY_MULTIPLY
             13 RETURN_VALUE
============================================================
                             c
============================================================
outer co_varnames = ('inner',)
outer co_cellvars = ()
outer co_freevars = ()
inner co_varnames = ()
inner co_cellvars = ()
inner co_freevars = ()
------------------------------------------------------------
inner code
------------------------------------------------------------
 21           0 LOAD_GLOBAL              0 (g)
              3 LOAD_CONST               1 (2)
              6 BINARY_MULTIPLY
              7 RETURN_VALUE

要するにローカル変数とは別に自由変数用の領域があるし、ロード/ストアの命令も別なのだな。