関数ステートメントが実行されると、それらは (字句的に) 囲んでいるスコープにバインドされます。
スニペットでは、ラムダはグローバル スコープにバインドされています。これfor
は、Python ではスイートが独立したスコープのユニットとして実行されないためです。for
ループの最後で、num
は外側のスコープにバインドされます。デモ:
for num in range(1, 6):
pass
assert num == 5 # num is now bound in the enclosing scope
したがって、for
ループ内で識別子をバインドすると、実際には囲んでいるスコープを操作しています。
for num in range(1, 6):
spam = 12
assert num == 5 # num is now bound in the enclosing scope
assert spam == 12 # spam is also bound in the enclosing scope
リスト内包表記についても同じです:
[num for num in range(1, 6)]
assert num == 5
心が吹いています、私は知っています。Anywho、私たちの新たな知識により、作成しているラムダがnum
、囲んでいるスコープにバインドされた (単一の) 識別子を参照していると判断できます。それはこれをより意味のあるものにするはずです:
functions = []
for number in range(1, 6):
def fun():
return number
functions.append(fun)
assert all(fun() == 5 for fun in functions)
assert all(fun() is number for fun in functions)
そして、これをさらに実証する最もクールな部分は次のとおりです。
# Same as above -- commented out for emphasis.
#functions = []
#for number in range(1, 6):
# def fun():
# return number
# functions.append(fun)
#assert all(fun() == 5 for fun in functions)
#assert all(fun() is number for fun in functions)
number = 6 # Rebind 6 in the scope and see how it affects the results.
assert all(fun() == 6 for fun in functions)
したがって、もちろん、これに対する解決策は、number
バインドするそれぞれに対して新しいエンクロージング スコープを作成することです。Python では、モジュール、クラス、および関数を含む新しいエンクロージング スコープを作成できます。別の関数を囲む新しいスコープを作成するためだけに関数を使用するのが一般的です。
Python では、クロージャは別の関数を返す関数です。関数コンストラクタのようなもの。get_fun
次の例で確認してください。
def get_fun(value):
""":return: A function that returns :param:`value`."""
def fun(): # Bound to get_fun's scope
return value
return fun
functions = []
for number in range(1, 6):
functions.append(get_fun(number))
assert [fun() for fun in functions] == range(1, 6)
は関数であるためget_fun
、独自の内部スコープを持つようになります。値を指定して呼び出すたびget_fun
に、その中のバインディングを追跡するために小さなテーブルが作成されます。つまり、「このスコープ内で、value
識別子は渡されたものを指している」と言います。そのスコープは、ぶらぶらする理由がない限り、関数の実行の最後に消えます。
スコープ内から関数を返す場合、それは「スコープ テーブル」の一部がハングアップする正当な理由です。返される関数は、後で呼び出すときに、そのスコープ テーブルから何かを参照する可能性があります。そのため、Pythonfun
内で が作成されたとき は、必要なときに便利なのスコープ テーブルについて説明します。get_fun
fun
get_fun
fun
実行モデルの Python ドキュメントで、詳細と技術用語 (少し柔らかくしています) について詳しく読むことができます。で関数が参照する囲みスコープの部分を見ることもできますprint fun.__closure__
。value
上記では、たまたま int であるへの参照が表示されます。
# Same as before, commented out for emphasis.
#functions = []
#for number in range(1, 6):
# functions.append(get_fun(number))
#assert [fun() for fun in functions] == range(1, 6)
print functions[0].__closure__
# Produces: (<cell at 0x8dc30: int object at 0x1004188>,)