もっと詳しく説明しようと思います。
もしあなたがそうするなら
i = 0
f = lambda: i
i
囲んでいるスコープの変数にアクセスする関数(ラムダは本質的に関数です)を作成します。
内部的には、i
. 大まかに言えば、異なる時点で異なる値を保持できる実数変数へのポインターのようなものです。
def a():
# first, yield a function to access i
yield lambda: i
# now, set i to different values successively
for i in range(100): yield
g = a() # create generator
f = next(g) # get the function
f() # -> error as i is not set yet
next(g)
f() # -> 0
next(g)
f() # -> 1
# and so on
f.func_closure # -> an object stemming from the local scope of a()
f.func_closure[0].cell_contents # -> the current value of this variable
ここで、 のすべての値はi
、その時点で、そのクロージャーに格納されます。関数f()
がそれらを必要とする場合。そこからそれらを取得します。
逆アセンブリ リストでその違いを確認できます。
これらは機能a()
を述べ、f()
次のように逆アセンブルします。
>>> dis.dis(a)
2 0 LOAD_CLOSURE 0 (i)
3 BUILD_TUPLE 1
6 LOAD_CONST 1 (<code object <lambda> at 0xb72ea650, file "<stdin>", line 2>)
9 MAKE_CLOSURE 0
12 YIELD_VALUE
13 POP_TOP
3 14 SETUP_LOOP 25 (to 42)
17 LOAD_GLOBAL 0 (range)
20 LOAD_CONST 2 (100)
23 CALL_FUNCTION 1
26 GET_ITER
>> 27 FOR_ITER 11 (to 41)
30 STORE_DEREF 0 (i)
33 LOAD_CONST 0 (None)
36 YIELD_VALUE
37 POP_TOP
38 JUMP_ABSOLUTE 27
>> 41 POP_BLOCK
>> 42 LOAD_CONST 0 (None)
45 RETURN_VALUE
>>> dis.dis(f)
2 0 LOAD_DEREF 0 (i)
3 RETURN_VALUE
b()
それを次のような関数と比較してください
>>> def b():
... for i in range(100): yield
>>> dis.dis(b)
2 0 SETUP_LOOP 25 (to 28)
3 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (100)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 11 (to 27)
16 STORE_FAST 0 (i)
19 LOAD_CONST 0 (None)
22 YIELD_VALUE
23 POP_TOP
24 JUMP_ABSOLUTE 13
>> 27 POP_BLOCK
>> 28 LOAD_CONST 0 (None)
31 RETURN_VALUE
ループの主な違いは
>> 13 FOR_ITER 11 (to 27)
16 STORE_FAST 0 (i)
b()
対で
>> 27 FOR_ITER 11 (to 41)
30 STORE_DEREF 0 (i)
in a()
:オブジェクト (クロージャ)STORE_DEREF
内のストア。(おそらく) 少し高速に動作する「通常の」変数を使用します。cell
STORE_FAST
ラムダも違いを生みます:
>>> dis.dis(lambda: i)
1 0 LOAD_GLOBAL 0 (i)
3 RETURN_VALUE
ここには がありますが、LOAD_GLOBAL
上記のものは を使用していますLOAD_DEREF
。後者も閉鎖用です。
のことをすっかり忘れていましlambda i=i: i
た。
値をデフォルト パラメータとして持っている場合は、完全に異なるパスを介して関数への道を見つけます。現在の値はi
、デフォルト パラメータを介して、作成したばかりの関数に渡されます。
>>> i = 42
>>> f = lambda i=i: i
>>> dis.dis(f)
1 0 LOAD_FAST 0 (i)
3 RETURN_VALUE
このようにして、関数は として呼び出されf()
ます。欠落している引数があることを検出し、それぞれのパラメーターにデフォルト値を入力します。これはすべて、関数が呼び出される前に発生します。関数内から、値が取得されて返されることがわかります。
そして、あなたのタスクを達成するためのさらに別の方法があります: 値を取るかのようにラムダを使用するだけです: lambda i: i
. これを呼び出すと、引数の欠落について文句を言います。
しかし、あなたはそれを使用して対処することができますfunctools.partial
:
ff = [functools.partial(lambda i: i, x) for x in range(100)]
ff[12]()
ff[54]()
このラッパーは、callable と渡されるいくつかの引数を取得します。結果のオブジェクトは、これらの引数と指定した引数を使用して元の callable を呼び出す callable です。ここで使用して、意図した値に固定しておくことができます。