7

私は非常に複雑なクラスを持っています:

class C:
    pass

そして、私はこのテストコードを持っています:

for j in range(10):
    c = C()
    print c

これは:

<__main__.C instance at 0x7f7336a6cb00>
<__main__.C instance at 0x7f7336a6cab8>
<__main__.C instance at 0x7f7336a6cb00>
<__main__.C instance at 0x7f7336a6cab8>
<__main__.C instance at 0x7f7336a6cb00>
<__main__.C instance at 0x7f7336a6cab8>
<__main__.C instance at 0x7f7336a6cb00>
<__main__.C instance at 0x7f7336a6cab8>
<__main__.C instance at 0x7f7336a6cb00>
<__main__.C instance at 0x7f7336a6cab8>

Pythonが2つの異なる値に切り替わることが簡単にわかります。場合によっては、これは壊滅的である可能性があります(たとえば、オブジェクトを他の複雑なオブジェクトに格納する場合)。

ここで、オブジェクトをリストに格納すると、次のようになります。

lst = []
for j in range(10):
    c = C()
    lst.append(c)
    print c

私はこれを手に入れます:

<__main__.C instance at 0x7fd8f8f7eb00>
<__main__.C instance at 0x7fd8f8f7eab8>
<__main__.C instance at 0x7fd8f8f7eb48>
<__main__.C instance at 0x7fd8f8f7eb90>
<__main__.C instance at 0x7fd8f8f7ebd8>
<__main__.C instance at 0x7fd8f8f7ec20>
<__main__.C instance at 0x7fd8f8f7ec68>
<__main__.C instance at 0x7fd8f8f7ecb0>
<__main__.C instance at 0x7fd8f8f7ecf8>
<__main__.C instance at 0x7fd8f8f7ed40>

これはケースを解決します。

だから今、私は質問をしなければなりません...誰かが複雑な言葉で(つまり、深く)Pythonがオブジェクト参照でどのように動作するかを説明できますか?私は、それは最適化の問題だと思います(メモリを節約するか、リークを防ぐために...)

どうもありがとうございます。

編集: わかりました、それでは、より具体的にしましょう。私はPythonが時々ゴミを収集しなければならないことをよく知っています...しかし、私の場合:

Cythonで定義されたクラスによって返されるリストがありました:ノードのリストを管理するクラス'ネットワーク'(ネットワークとノードクラスの両方がで定義されていますCython extension)。各ノードにはオブジェクトがあります[次に(void *)にキャストされます]'userdata'オブジェクト。ノードリストはcython内から入力され、UserDataはPythonスクリプト内から入力されます。したがって、Pythonでは、次のようになりました。

...
def some_python_class_method(self):
    nodes = self.netBinding.GetNetwork().get_nodes()
    ...
    for item in it:
        a_site = PyLabSiteEvent()
        #l_site.append(a_site)        # WARN : Required to get an instance on 'a_site' 
                                      #        that persits - workaround...
    item.SetUserData(a_site)

後で同じcythongetterを使用して同じPythonクラスでこのノードリストを再利用します。

def some_other_python_class_method(self, node):
    s_data = node.GetUserData()
    ...

したがって、ノードリストのUserDatasで作成されたストレージでは、私のPythonスクリプトは完全にブラインドであり、メモリを解放/再利用していたようです。これは、追加のリスト(ここでは'l_site')を使用して、2回目(ただし、Python側では明らかに1回目)を参照することで機能しました。これが私がPython自体についてもう少し知る必要があった理由ですが、私がPython間の通信を実装した方法はCython、直面しなければならなかった問題に責任があるようです。

4

3 に答える 3

13

ここで「複雑」である必要はありません。最初の例では、「c」という名前で参照されるオブジェクトへの他の参照を保持しません。ループすると、以前に「c」に保持されていた 1 つの参照が失われます。

標準の Python は参照カウントを使用してメモリからオブジェクトを削除するタイミングを追跡するため、この時点で前のループ反復のオブジェクトの参照カウントが 0 に達すると、そのオブジェクトは破棄され、そのメモリは他のオブジェクトで使用できるようになります。

なぜ2つの変化する値があるのですか? =新しい反復のオブジェクトが作成された時点で (つまり、Python がinの右側で式を実行するときc = C())、前の反復のオブジェクトがまだ存在し、名前で参照されているcため、新しいオブジェクトは別のメモリに構築されます。場所。次に、Python は新しいオブジェクトの割り当てに進み、cその時点で前のオブジェクトは上記のように破棄されます。これは、次の (3 回目の) 反復で、そのメモリが の新しいインスタンスに使用できることを意味しますC

2 番目の例では、新しく作成されたオブジェクトが参照を失うことはありません。したがって、それらのメモリはまったく解放されません。新しいオブジェクトは常に新しいメモリ ロケーションを占有します。

最も重要なのは 、Python などの高級言語を使用する目的は、メモリ割り当てについて心配する必要がないことです。言語はそれを処理します。この場合、お気づきのように、CPython (標準) 実装は適切に機能します。Pypy や Jython などの他の実装では、上記の例の各インスタンスの「メモリ位置」に関して完全に異なる動作をすることができますが、すべての準拠する実装 (これら 3 つを含む) は、「観点」からはまったく同じように動作します。 Python プログラム: (1) 参照を保持しているインスタンスにアクセスできます。(2) これらのインスタンスのデータはいずれにしても破損したり破損したりしません。

于 2012-10-11T02:29:38.640 に答える
5

複雑ではないようです。

最初の例では、2 回目のループでは、0x7f7336a6cb00 のメモリが、最初の繰り返しで作成された C インスタンスによって占有されています。したがって、Python は次のメモリ ブロック 0x7f7336a6cab8 を新しい C オブジェクトに割り当てます。

ただし、2 番目の C オブジェクトを作成して に割り当てるとすぐにc、0x7f7336a6cb00 の孤立したオブジェクトへの参照は残りません。したがって、ループの3回目では、Python はこの場所のメモリを新しいオブジェクトに再利用できます。もちろん、そうするとすぐに、0x7f7336a6cab8 のオブジェクトはそれへの参照を失い、そのメモリ位置はループを介して4回目のリサイクルに使用できるようになります。

ただし、2 番目の例では、オブジェクトをリストに追加することで、作成した各オブジェクトへの参照を保持しています。これらのオブジェクトは常に少なくとも 1 つの参照を持っているため、オブジェクトが「存在する」メモリを解放して再利用することはできません。したがって、Python は毎回新しいメモリを割り当てます。

最初の例で生み出された危険の錯覚はまさにそれ、錯覚です。作成したオブジェクトへの参照が存在する限り、オブジェクトは維持されます。参照が存在しなくなったら、プログラムがオブジェクトを使用できなくなるため、Python がオブジェクトによって使用されているメモリを解放しても安全です。

于 2012-10-11T02:33:21.413 に答える
3

各ループ中に、オブジェクトのc参照を変更するため、元のオブジェクトにアクセスできなくなり、Pythonはそれを自由に削除できます(二度とアクセスできない場合は、オブジェクトを保持する必要があるため)。その時点で、同じ場所に空きメモリがあり、それを再利用しているようです。これについて何が驚くべきかわかりません。インタプリタがメモリを再利用しなかった場合、非常に速く使い果たされます。

オブジェクトはまだアクセス可能であるため、リストにオブジェクトを追加するときにこれは発生しません。そのため、Pythonはオブジェクトを削除できません(再度使用する可能性があるため)。

Pythonはオブジェクトを使用できる間はオブジェクトを削除しないため、これによって問題が発生することはありません。したがって、「オブジェクトを複雑なオブジェクトに保存」すると、オブジェクトは引き続きアクセス可能になり、メモリは再利用されません。 (少なくともオブジェクトが消えるまで)。

于 2012-10-11T02:27:57.613 に答える