コンパイラはそれらを接続します。
と をコンパイルするouter
とinner
、 のfunc
名前outer
はクロージャとしてマークされ、 では自由変数としてマークされますinner
。実行時に、インタープリターはfunc
名前outer
への参照をinner
クロージャ セルとして関数オブジェクトにアタッチすることを認識します。
バイトコードの逆アセンブルで結果を確認できます。
>>> import dis
>>> dis.dis(outer)
2 0 LOAD_CLOSURE 0 (func)
3 BUILD_TUPLE 1
6 LOAD_CONST 1 (<code object inner at 0x1079a5ab0, file "<stdin>", line 2>)
9 MAKE_CLOSURE 0
12 STORE_FAST 1 (inner)
4 15 LOAD_FAST 1 (inner)
18 RETURN_VALUE
>>> inner = outer(lambda a, b: None)
>>> dis.dis(inner)
3 0 LOAD_DEREF 0 (func)
3 LOAD_GLOBAL 0 (arg_inner)
6 LOAD_FAST 0 (foo)
9 CALL_FUNCTION 2
12 POP_TOP
13 LOAD_CONST 0 (None)
16 RETURN_VALUE
は、使用するために名前をクロージャーでLOAD_CLOSURE
ラップします。タプルとして付加されたクロージャ セルを持つ関数オブジェクトを ( でロードされたバイト コード オブジェクトから) 構築します。では、オペコードがクロージャから値をロードします。func
inner
MAKE_CLOSURE
LOAD_CONST
inner
LOAD_DEREF
func
inner
クロージャーは、結果の関数オブジェクトで見つけることができます。
>>> inner.func_closure
(<cell at 0x107a25a28: function object at 0x107a28b18>,)
>>> inner.func_code.co_freevars
('func',)
>>> inner.func_closure[0].cell_contents
<function <lambda> at 0x107a28b18>
したがって、inner
関数オブジェクトは、後で逆参照するためにクロージャを持ちます。