10

私がクラスAを持っているとしましょう:

class A(object):
    def __init__(self, x):
        self.x = x

    def __str__(self):
        return self.x

そして、私はテイクsys.getsizeofのインスタンスのバイト数を確認するために使用します。A

>>> sys.getsizeof(A(1))
64
>>> sys.getsizeof(A('a'))
64
>>> sys.getsizeof(A('aaa'))
64

上記の実験で示したように、Aオブジェクトのサイズは何であっても同じself.xです。

では、Pythonがオブジェクトを内部的にどのように保存するのだろうか?

4

2 に答える 2

27

それは、オブジェクトの種類と、Pythonの実装によって異なります:-)

ほとんどの人が使用するときに使用するCPythonではpython、すべてのPythonオブジェクトはC構造体で表されますPyObject。'オブジェクトを格納する'すべてが実際にを格納しPyObject *ます。PyObject構造体は最小限の情報を保持します。オブジェクトの型(別の型へのポインタ)PyObjectとその参照カウント(ssize_tサイズの整数)。Cで定義された型は、オブジェクト自体に格納する必要のある追加情報でこの構造体を拡張し、場合によっては割り当てます。個別に追加データ。

たとえば、タプル(PyTupleObjectPyObject構造体の「拡張」として実装)は、その長さと、PyObject構造体自体の内部に含まれるポインターを格納します(構造体には、定義に1つの長さの配列が含まれますが、実装は、構造体を保持するのに適切なサイズにPyTupleObject加えて、タプルが保持する必要のあるアイテムとまったく同じ数のアイテム。)同様に、文字列(PyStringObject)は、長さ、キャッシュされたハッシュ値、文字列キャッシング(「インターン」)ブックキーピング、およびの実際の文字*を格納します。彼らのデータ。したがって、タプルと文字列は単一のメモリブロックです。

一方、リスト(PyListObject)は、長さ、データ用のa、およびデータに割り当てたスペースの量を追跡するためのPyObject **別の長さを格納します。ssize_tPythonはPyObjectどこにでもポインターを格納するため、割り当てられたPyObject構造体を拡張することはできません。そのため、構造体を移動する必要があります。つまり、すべてのポインターを見つけて更新する必要があります。リストは拡大する必要がある場合があるため、PyObject構造体とは別にデータを割り当てる必要があります。タプルとストリングは成長できないため、これは必要ありません。ディクト(PyDictObject)は同じように機能しますが、アイテムだけでなく、キー、値、およびキーのキャッシュされたハッシュ値を格納します。Dictには、小さなdictや特殊なルックアップ関数に対応するための追加のオーバーヘッドもあります。

ただし、これらはすべてCのタイプであり、通常、Cソースを確認するだけでどれだけのメモリを使用するかを確認できます。CではなくPythonで定義されたクラスのインスタンスはそれほど簡単ではありません。最も単純なケースであるクラシッククラスのインスタンスはそれほど難しくありません。それはPyObject、そのクラス(これは、構造体にすでにPyObject *格納されているタイプと同じものではありません)に、その属性(他のすべてのインスタンス属性を保持します)に格納します。 )およびそのweakreflistへのa(モジュールによって使用され、必要な場合にのみ初期化されます。)インスタンスのPyObjectPyObject *__dict__PyObject *weakref__dict__通常はインスタンスに固有であるため、このようなインスタンスの「メモリサイズ」を計算するときは、通常、属性dictのサイズもカウントする必要があります。ただし、インスタンスに固有である必要はありません。__dict__うまく割り当てることができます。

新しいスタイルのクラスはマナーを複雑にします。従来のクラスとは異なり、新しいスタイルのクラスのインスタンスは個別のCタイプではないため、オブジェクトのクラスを個別に格納する必要はありません。それらには、weakreflist参照の余地がありますが、従来のインスタンスとは異なり、任意の属性の属性を必要と__dict__しません。クラス(およびそのすべての基本クラス)が属性の厳密なセットを定義するために使用し、それらの属性のいずれにも名前が付けられていない場合、インスタンスは任意の属性を許可せず、dictは割り当てられません。一方、によって定義された属性は、どこかに格納する必要があります。これは、__dict____slots____dict____slots__PyObjectこれらの属性の値へのポインタは、Cで記述された型で行われるのと同じように、PyObject構造体で直接行われます。したがって、の各エントリは、属性が設定されているかどうかに関係なく、__slots__を取ります。PyObject *

とはいえ、Pythonのすべてがオブジェクトであり、オブジェクトを保持するすべてが参照を保持するだけなので、オブジェクト間に線を引くことが非常に難しい場合があるという問題が残ります。2つのオブジェクトが同じビットのデータを参照できます。それらは、そのデータへの2つの参照のみを保持する場合があります。両方のオブジェクトを削除すると、データも削除されます。彼らは両方ともデータを所有していますか?それらの1つだけですが、もしそうなら、どれですか?または、1つのオブジェクトを削除してもデータの半分が解放されない場合でも、データの半分を所有していると思いますか?弱点はこれをさらに複雑にする可能性があります。2つのオブジェクトが同じデータを参照できますが、一方のオブジェクトを削除すると、もう一方のオブジェクトそのデータへの参照を削除し、結局データがクリーンアップされる可能性があります。

幸いなことに、一般的なケースはかなり簡単に理解できます。heapyのように、これらのことを追跡するのに合理的な仕事をするPython用のメモリデバッガがあります。また、クラス(およびその基本クラス)が適度に単純である限り、特に多数の場合に、どれだけのメモリを使用するかについて、知識に基づいて推測することができます。データ構造の正確なサイズを本当に知りたい場合は、CPythonソースを参照してください。Include/<type>object.hほとんどの組み込み型は、で説明および実装されている単純な構造体Objects/<type>object.cです。PyObject構造体自体はで説明されていInclude/object.hます。覚えておいてください:それはずっと下のポインタです。それらも部屋を取ります。

于 2010-10-31T11:26:16.170 に答える
0

新しいクラスインスタンスの場合、getsizeof()は、C関数PyInstance_New()によって返されるPyObjectへの参照のサイズを返します。

すべてのオブジェクトサイズのリストが必要な場合は、これを確認してください。

于 2010-10-31T11:07:02.957 に答える