0

この質問は、Python がエスケープ解析をどの程度賢く行うかに関するものです。

次のプログラムがあるとしましょう:

class Dog():
  breed = 'electronic dog'
  collar_type = 'microsoft'

sparky=Dog()
def get_dog_info():
  return sparky.breed

関数 get_dog_info() は、明らかに sparky.breed を閉じる必要があります。しかし、そうするために、実装は Dog オブジェクト全体もエスケープしますか? つまり、collar_type を閉じるために追加のメモリ コストがかかるのでしょうか? それとも、これは実装に任されている選択ですか?

4

3 に答える 3

4

Dogとの両方sparkyが、モジュールを形成するグローバル名前空間によって参照され、両方をメモリに保持します。

を実行した場合del Dogでもsparky、クラスを (その参照を通じて__class__) 参照し、それを維持します。クラスは、その定義の一部である 2 つの属性を参照するため、それらも有効に保たれます。これはすべて関数から独立していget_dog_infoます。

CPython は、参照カウントに基づいてオブジェクトをメモリに保持します。Pythonの何かがオブジェクトの参照を開始すると、そのオブジェクトの参照カウントが 1 増加し、参照が削除されると再び減少します。カウントが 0 になると、オブジェクトはメモリから削除され、必要に応じてガベージ コレクション プロセスによって循環参照が分割され、このプロセスが容易になります。

sparkyはグローバルであるため、関数コードは何も直接参照しないことに注意してください。グローバルは実行時に検索されます。あなたも削除するsparkyと、すべての参照がクリアされます。sparkyinget_dog_info()はグローバル名前空間で検索されるため、呼び出すget_dog_info()NameError.

クロージャー (親関数スコープ内の変数への参照) がある場合、同じ規則が適用されますクロージャー参照はインスタンスへの別の参照としてカウントされるため、間接的にクラスおよび含まれる属性にカウントされます。

したがって、クロージャーを作成する次の例を検討してください。

class Dog():
    breed = 'electronic dog'
    collar_type = 'microsoft'

def foo():
    sparky = Dog()
    def bar():
        return sparky.breed
    return bar

bar = foo()
del Dog

上記の例では、クロージャが引き続きそのクラスのインスタンスを参照しているDogため、クラスはメモリ内に残ります。bar

>>> bar.__closure__
(<cell at 0x1012b2280: Dog object at 0x1012b5110>,)
>>> bar.__closure__[0].cell_contents
<__main__.Dog object at 0x1012b5110>
>>> bar()
'electronic dog'
于 2013-02-20T16:52:47.610 に答える
2

Martijnの回答の補足として、Dogオブジェクト(sparky)が文字列()ではなくクロージャに格納される理由について次のように追加しますsparky.breed。これは少なくともあなたの質問の一部だと思います。

これは、.演算子の動作方法が原因です。関数の呼び出し時にのbreed属性にアクセスするため、オブジェクト全体を格納する必要があります。文字列のみをクロージャに格納する場合は、文字列を直接参照するように関数コードを変更する必要があります。sparkysparky

つまり、次のようになります...

>>> class Dog():
...   breed = 'electronic dog'
...   collar_type = 'microsoft'
... 
>>> def get_dog_info_closure():
...     sparky = Dog()
...     def get_dog_info():
...         return sparky.breed
...     return get_dog_info
>>> get_dog_info = get_dog_info_closure()

...関数のクロージャには、:Dogによって返される文字列ではなく、オブジェクトが含まれていることがわかります。sparky.breed

>>> get_dog_info.func_closure
(<cell at 0x10049fa28: instance object at 0x1004a1cf8>,)
>>> get_dog_info.func_closure[0].cell_contents
<__main__.Dog instance at 0x1004a1cf8>

これは、オブジェクトを取得してDog変更できることを意味し、将来の呼び出しにはその変更が反映されます。

>>> get_dog_info.func_closure[0].cell_contents.breed = ('actual '
                                                        'flesh-and-blood dog!')
>>> get_dog_info()
'actual flesh-and-blood dog!'

文字列のみを保存するbreedには、個別に参照する必要があります。

>>> def get_dog_info_closure():
...     sparky = Dog()
...     sbreed = sparky.breed
...     def get_dog_info():
...         return sbreed
...     return get_dog_info
... 
>>> get_dog_info = get_dog_info_closure()
>>> get_dog_info.func_closure[0].cell_contents
'electronic dog'
于 2013-02-20T20:57:01.307 に答える
1

明らかに、あなたが私たちに示したコードには、クロージャーがまったくありません( globals による)。単なる断片だと思います。このコードを見てください(例として):

def test():
  class Dog():
    breed = 'electronic dog'
    collar_type = 'microsoft'

  sparky=Dog()
  def get_dog_info():
    return sparky.breed

  print get_dog_info.func_closure

test()

これは、オブジェクト全体sparkyが で「閉じられた」ことを示していますget_dog_info。オブジェクトの属性を取得するには、オブジェクトに関するある程度の知識が必要なためです (breedたとえば、プロパティにすることができます)。だから、そこを改善する余地はありません。

于 2013-02-20T16:48:32.323 に答える