web-dev-qa-db-ja.com

obj .__ closure__には正確に何が含まれていますか?

Beazley pg 100の言及:

>>>python.__closure__
(<cell at 0x67f50: str object at 0x69230>,)
>>>python.__closure__[0].cell_contents

私の理解では、__closure__はリストですが、このセルとstrオブジェクトは何ですか?それは1-aryタプルのように見えますか?

36
user621819

クロージャーセルは、関数に必要な値を参照しますが、周囲のスコープから取得されます。

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つのクロージャセルがあり、関数spamfooを指します。セルは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つのクロージャはすべてその値に解決されます。

46
Martijn Pieters

ネストされた関数(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
()
1
lyu.l
>>> 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タイプが他の場所で使用されているのを見たことがありません。クロージャー変数を保持するために設計されているようです。

1
XORcist