Pythonのクロージャは、まさしくこのように動作します。
すべてのフリー変数(ローカル変数でも変数でもない変数)は、内部の__closure__
プロパティに保存されます。
In [1394]: def make_counter():
...: count = 0
...: def counter():
...: nonlocal count
...: count += 1
...: return count
...: return counter
...:
In [1395]: f = make_counter()
In [1396]: f
Out[1396]: <function __main__.make_counter.<locals>.counter>
In [1397]: f.__closure__
Out[1397]: (<cell at 0x10af4bd98: int object at 0x10024ac20>,)
In [1399]: f.__closure__[0].cell_contents
Out[1399]: 0
ちょうどので、最初のケースでは、int
sが不変である、ということが起こるので、あなたは、元のcount
を変更されていません。
f
が呼び出されると、内部関数にはローカル変数count
を含む独自の環境フレームが作成され、__closure__
とマージされます。
はf
を呼び出してみて、カウンタがインクリメントされます。後者の場合
In [1400]: f()
Out[1400]: 1
In [1401]: f.__closure__[0].cell_contents
Out[1401]: 1
、あなたは変更可能で、リスト内のエントリを変更しています。参照は__closure__
プロパティにコピーされます。ところで
は、make_counter
はグローバル関数であるので、空__closure__
属性を有する:
In [1403]: print(make_counter.__closure__)
None
あなたの第二の例を、少し改変:
In [1404]: def make_counter():
...: count = [0]
...: print(id(count))
...: def counter():
...: nonlocal count
...: count[0] += 1
...: return count[0]
...: return counter
...:
In [1405]: f = make_counter()
4495587464
In [1408]: id(f.__closure__[0].cell_contents)
Out[1408]: 4495587464
id
であります同一。