Beazley pg 100の言及:
>>>python.__closure__
(<cell at 0x67f50: str object at 0x69230>,)
>>>python.__closure__[0].cell_contents
私の理解では、__closure__
はリストですが、このセルとstrオブジェクトは何ですか?それは1-aryタプルのように見えますか?
クロージャーセルは、関数に必要な値を参照しますが、周囲のスコープから取得されます。
Pythonがネストされた関数をコンパイルするとき、それが参照する変数を記録しますが、ネストされた関数と親スコープの両方のコードオブジェクトの親関数(グローバルではない)でのみ定義されます。これらの関数の_co_freevars
_オブジェクトの_co_cellvars
_属性と___code__
_属性です。
次に、実際にcreateネストされた関数(親関数が実行されたときに発生します)を実行すると、これらの参照を使用して、ネストされた関数にクロージャがアタッチされます。
関数クロージャは、自由変数(_co_freevars
_で指定)ごとに1つずつ、セルのタプルを保持します。セルは、親スコープのローカル変数への特別な参照であり、それらのローカル変数が指す値に従います。これは、例で最もよく説明されています。
_def foo():
def bar():
print(spam)
spam = 'ham'
bar()
spam = 'eggs'
bar()
return bar
b = foo()
b()
_
上記の例では、関数bar
には1つのクロージャセルがあり、関数spam
のfoo
を指します。セルはspam
の値に従います。さらに重要なことに、foo()
が完了し、bar
が返されると、内部の変数eggs
であっても、セルは値(文字列spam
)を参照し続けます。 foo
はもう存在しません。
したがって、上記のコードは次のように出力します。
_>>> b=foo()
ham
eggs
>>> b()
eggs
_
_b.__closure__[0].cell_contents
_は_'eggs'
_です。
クロージャが逆参照されることに注意してくださいbar()
が呼び出されたとき;クロージャーはここで値をキャプチャしません。これは、ループ変数を参照するネストされた関数(lambda
式またはdef
ステートメントを使用)を生成するときに違いを生みます。
_def foo():
bar = []
for spam in ('ham', 'eggs', 'salad'):
bar.append(lambda: spam)
return bar
for bar in foo():
print bar()
_
3つのsalad
関数はすべてlambda
変数を参照し、関数オブジェクトの作成時にバインドされた値ではないため、上記はspam
を3回続けて出力します。 for
ループが終了するまでに、spam
は_'salad'
_にバインドされていたため、3つのクロージャはすべてその値に解決されます。
ネストされた関数(closure
)がPythonで定義されている場合:
外部関数はco_cellvars
を使用して、内部関数によって参照される可能性のある外部関数で定義された変数に注意します。
内部関数はco_freevars
を使用して、後で使用するために参照できる外部関数で定義された変数を記録します。
例:
# python3
Python 3.4.5 (default, May 29 2017, 15:17:55)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-11)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def foo(n):
... a = 1
... def g(n):
... return a - n
... return g
...
>>> foo.__closure__
>>> foo.__code__.co_freevars
()
>>> foo.__code__.co_cellvars
('a',)
>>> foo(0).__closure__
(<cell at 0x7f2cd98db1c8: int object at 0x7f2cd9847960>,)
>>> foo(0).__closure__[0].cell_contents
1
>>> foo(0).__code__.co_freevars
('a',)
>>> foo(0).__code__.co_cellvars
()
>>> def f():
... a = "HELO"
... b = 1.0
... def w(c):
... return a,b,c
... return w
>>> w = f()
>>> w.__closure__
(<cell at 0xa05c4ac: str object at 0x9e91b74>, <cell at 0xa05c3bc: float object at 0xb733dde8>)
>>> w.__closure__[0].cell_contents
'HELO'
>>> w.__closure__[1].cell_contents
1.0
cellタイプが他の場所で使用されているのを見たことがありません。クロージャー変数を保持するために設計されているようです。