Pythonでオブジェクトのサイズを確認するにはどうすればよいですか?
「ただsys.getsizeofを使用する」という答えは完全な答えではありません。 
その答えは組み込みオブジェクトに対して直接機能しますが、それらのオブジェクトに含まれる可能性のあるもの、特にカスタムオブジェクト、タプル、リスト、ディクショナリ、セットなどのタイプに含まれるものは考慮されません。これらには、相互にインスタンスを含めることができ、数値、文字列、その他のオブジェクトを含めることができます。
より完全な答え
Anacondaディストリビューションの64ビットPython 3.6とsys.getsizeofを使用して、次のオブジェクトの最小サイズを決定しました。また、セットとディクテーションによって事前に割り当てられたスペースが空になるので、設定された量になるまで(つまり、言語の実装によって異なります):
Python 3:
Empty
Bytes  type        scaling notes
28     int         +4 bytes about every 30 powers of 2
37     bytes       +1 byte per additional byte
49     str         +1-4 per additional character (depending on max width)
48     tuple       +8 per additional item
64     list        +8 for each additional
224    set         5th increases to 736; 21nd, 2272; 85th, 8416; 341, 32992
240    dict        6th increases to 368; 22nd, 1184; 43rd, 2280; 86th, 4704; 171st, 9320
136    func def    does not include default args and other attrs
1056   class def   no slots 
56     class inst  has a __dict__ attr, same scaling as dict above
888    class def   with slots
16     __slots__   seems to store in mutable tuple-like structure
                   first slot grows to 48, and so on.
これをどのように解釈しますか?10個のアイテムが入ったセットがあるとしましょう。各アイテムがそれぞれ100バイトの場合、データ構造全体の大きさはどれくらいですか?セットのサイズは736バイトなので、一度にサイズが736バイトになっています。次に、アイテムのサイズを追加すると、合計で1736バイトになります。
関数とクラスの定義に関する注意事項:
各クラス定義には、__dict__クラス属性のプロキシ(48バイト)構造があることに注意してください。各スロットpropertyのクラス定義には(のような)記述子があります。
スロット付きインスタンスは、最初の要素が48バイトで始まり、追加ごとに8バイトずつ増加します。空のスロットオブジェクトのみが16バイトであり、データのないインスタンスはほとんど意味がありません。
また、各関数の定義には、コードオブジェクト(おそらくdocstrings)やその他の可能な属性が含まれます__dict__。
また、我々が使用していることに注意してくださいsys.getsizeof()、我々はオブジェクトのガベージコレクションのオーバーヘッド、含まマージナルスペースの使用状況、気にするので、ドキュメントからの:
  getsizeof()はオブジェクトの__sizeof__メソッドを呼び出し、オブジェクトがガベージコレクターによって管理されている場合は、ガベージコレクターのオーバーヘッドを追加します。
また、リストのサイズを変更すると(リストに繰り返し追加するなど)、セットや辞書と同様に、スペースが事前に割り当てられます。listobj.cソースコード:
    /* This over-allocates proportional to the list size, making room
     * for additional growth.  The over-allocation is mild, but is
     * enough to give linear-time amortized behavior over a long
     * sequence of appends() in the presence of a poorly-performing
     * system realloc().
     * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
     * Note: new_allocated won't overflow because the largest possible value
     *       is PY_SSIZE_T_MAX * (9 / 8) + 6 which always fits in a size_t.
     */
    new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);
歴史的なデータ
guppy.hpyおよびで確認されたPython 2.7分析sys.getsizeof:
Bytes  type        empty + scaling notes
24     int         NA
28     long        NA
37     str         + 1 byte per additional character
52     unicode     + 4 bytes per additional character
56     tuple       + 8 bytes per additional item
72     list        + 32 for first, 8 for each additional
232    set         sixth item increases to 744; 22nd, 2280; 86th, 8424
280    dict        sixth item increases to 1048; 22nd, 3352; 86th, 12568 *
120    func def    does not include default args and other attrs
64     class inst  has a __dict__ attr, same scaling as dict above
16     __slots__   class with slots has no dict, seems to store in 
                   mutable tuple-like structure.
904    class def   has a proxy __dict__ structure for class attrs
104    old class   makes sense, less stuff, has real dict though.
辞書(セットではない)は、Python 3.6でよりコンパクトに表現されていることに注意してください。
参照する追加のアイテムごとに8バイトは、64ビットマシンでは非常に理にかなっていると思います。これらの8バイトは、格納されているアイテムが存在するメモリ内の場所を指します。Python 2では、4バイトはユニコードの固定幅ですが、Python 3では、strは文字の最大幅に等しい幅のユニコードになります。
(そしてスロットの詳細については、この回答を参照してください)
より完全な機能
リスト、タプル、セット、ディクショナリ、、、obj.__dict__およびobj.__slots__まだ考えていないその他の要素を検索する関数が必要です。
gc.get_referentsこの検索はCレベルで機能するため(非常に高速になるため)、信頼できる検索を行いたいと考えています。欠点は、get_referentsが冗長なメンバーを返す可能性があるため、二重にカウントしないようにする必要があります。
クラス、モジュール、関数はシングルトンです-それらはメモリ内に一度存在します。それらについて私たちができることはほとんどないので、私たちはそれらのサイズにそれほど興味はありません-それらはプログラムの一部です。したがって、それらがたまたま参照された場合、それらをカウントすることは避けます。
タイプのブラックリストを使用するので、プログラム全体をサイズカウントに含めません。
import sys
from types import ModuleType, FunctionType
from gc import get_referents
# Custom objects know their class.
# Function objects seem to know way too much, including modules.
# Exclude modules as well.
BLACKLIST = type, ModuleType, FunctionType
def getsize(obj):
    """sum size of object & members."""
    if isinstance(obj, BLACKLIST):
        raise TypeError('getsize() does not take argument of type: '+ str(type(obj)))
    seen_ids = set()
    size = 0
    objects = [obj]
    while objects:
        need_referents = []
        for obj in objects:
            if not isinstance(obj, BLACKLIST) and id(obj) not in seen_ids:
                seen_ids.add(id(obj))
                size += sys.getsizeof(obj)
                need_referents.append(obj)
        objects = get_referents(*need_referents)
    return size
これを次のホワイトリスト関数と対比すると、ほとんどのオブジェクトは、ガベージコレクションの目的で自分自身をトラバースする方法を知っています(これは、特定のオブジェクトがメモリ内でどれほど高価であるかを知りたいときにおおよそ探しているものです。この機能は、gc.get_referents。)ただし、この措置は、注意しないと、意図した範囲よりもはるかに広範囲になります。
たとえば、関数は、それらが作成されたモジュールについてかなり知っています。
対照的なもう1つのポイントは、辞書のキーである文字列は通常インターンされるため、重複しないことです。以下のためにチェックすることid(key)も私たちは、次のセクションで行うカウントの重複を回避することができます。ブラックリストソリューションは、文字列であるキーのカウントをすべてスキップします。
ホワイトリストに登録されたタイプ、再帰的なビジター(古い実装)
これらのタイプのほとんどを自分でカバーするために、gcモジュールに依存する代わりに、この再帰関数を作成して、ほとんどの組み込みオブジェクト、コレクションモジュールのタイプ、カスタムタイプ(スロットなど)を含む、ほとんどのPythonオブジェクトのサイズを推定しようとしました。
この種の関数は、メモリ使用量として数えるタイプをはるかに細かく制御しますが、タイプを除外する危険があります。
import sys
from numbers import Number
from collections import Set, Mapping, deque
try: # Python 2
    zero_depth_bases = (basestring, Number, xrange, bytearray)
    iteritems = 'iteritems'
except NameError: # Python 3
    zero_depth_bases = (str, bytes, Number, range, bytearray)
    iteritems = 'items'
def getsize(obj_0):
    """Recursively iterate to sum size of object & members."""
    _seen_ids = set()
    def inner(obj):
        obj_id = id(obj)
        if obj_id in _seen_ids:
            return 0
        _seen_ids.add(obj_id)
        size = sys.getsizeof(obj)
        if isinstance(obj, zero_depth_bases):
            pass # bypass remaining control flow and return
        elif isinstance(obj, (tuple, list, Set, deque)):
            size += sum(inner(i) for i in obj)
        elif isinstance(obj, Mapping) or hasattr(obj, iteritems):
            size += sum(inner(k) + inner(v) for k, v in getattr(obj, iteritems)())
        # Check for custom object instances - may subclass above too
        if hasattr(obj, '__dict__'):
            size += inner(vars(obj))
        if hasattr(obj, '__slots__'): # can have __slots__ with __dict__
            size += sum(inner(getattr(obj, s)) for s in obj.__slots__ if hasattr(obj, s))
        return size
    return inner(obj_0)
そして私はそれをかなりカジュアルにテストしました(私はそれを単体テストする必要があります):
>>> getsize(['a', tuple('bcd'), Foo()])
344
>>> getsize(Foo())
16
>>> getsize(tuple('bcd'))
194
>>> getsize(['a', tuple('bcd'), Foo(), {'foo': 'bar', 'baz': 'bar'}])
752
>>> getsize({'foo': 'bar', 'baz': 'bar'})
400
>>> getsize({})
280
>>> getsize({'foo':'bar'})
360
>>> getsize('foo')
40
>>> class Bar():
...     def baz():
...         pass
>>> getsize(Bar())
352
>>> getsize(Bar().__dict__)
280
>>> sys.getsizeof(Bar())
72
>>> getsize(Bar.__dict__)
872
>>> sys.getsizeof(Bar.__dict__)
280
この実装は、クラス定義と関数定義に分類されます。すべての属性を追跡するわけではないためですが、これらはプロセスのメモリに1回だけ存在する必要があるため、サイズはそれほど重要ではありません。