Pythonでオブジェクトのサイズを確認するにはどうすればよいですか?


682

Pythonで文字列や整数などのオブジェクトのサイズを取得する方法を知りたいです。

関連質問:Pythonリスト(タプル)には要素ごとに何バイトありますか?

値のサイズを指定するサイズフィールドを含むXMLファイルを使用しています。このXMLを解析してコーディングを行う必要があります。特定のフィールドの値を変更する場合は、その値のサイズフィールドを確認します。ここで、入力するゴングの新しい値がXMLと同じサイズかどうかを比較します。新しい値のサイズを確認する必要があります。紐の場合はその長さと言えます。しかし、int、floatなどの場合、私は混乱しています。

回答:


665

モジュールで定義されているsys.getsizeof関数を使用するだけsysです。

sys.getsizeof(object[, default])

オブジェクトのサイズをバイト単位で返します。オブジェクトはどのタイプのオブジェクトでもかまいません。すべての組み込みオブジェクトは正しい結果を返しますが、これは実装固有であるため、サードパーティの拡張機能に当てはまる必要はありません。

default引数はオブジェクト型のサイズを取得するための手段を提供していないと、原因となる場合に返される値を定義することができます TypeError

getsizeofオブジェクトの__sizeof__メソッドを呼び出し、オブジェクト がガベージコレクターによって管理されている場合は、ガベージコレクターのオーバーヘッドを追加します。

Python 3.0での使用例:

>>> import sys
>>> x = 2
>>> sys.getsizeof(x)
24
>>> sys.getsizeof(sys.getsizeof)
32
>>> sys.getsizeof('this')
38
>>> sys.getsizeof('this also')
48

Python <2.6で作業していない場合は、代わりにこの拡張モジュールをsys.getsizeof使用できます。それを使用したことはありません。


181
それはなどのリストにネストされたオブジェクトまたはネストされたdictsまたはdictsのために当てはまらないことが免責事項に追加してください
JohnnyM

8
@ChaimGこれは、すべてのオブジェクトが32バイトしか使用しないためです。残りは他のオブジェクトへの参照です。参照されるオブジェクトを考慮したい場合__sizeof__は、クラスのメソッドを定義する必要があります。組み込みのdictpythonクラスで定義されているため、typeのオブジェクトを使用すると正しい結果が得られますdict
nosklo 2017

19
この作業の免責事項と例外getsizeofは、ほとんど価値のない機能を箱から出しているほとんどすべてのユースケースをカバーしています。
ロビノ2017年

7
整数2が24バイトで格納されるのはなぜですか?
Saher Ahwal

4
@SaherAhwalそれは単なる整数ではなく、メソッド、属性、アドレスを持つ完全なオブジェクト...
nosklo

369

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回だけ存在する必要があるため、サイズはそれほど重要ではありません。


5
この答えはCPython(Anacondaを介してPythonを取得することによって暗示される)に固有であると追加するかもしれません
gerrit

1
CPythonはリファレンス実装であり、同じAPIを提供するjythonのオンラインドキュメントを確認しただけなので、APIを実装している限り、これは他の実装でも機能すると信じています。
アーロンホール

私にとっては、マスクされたおよびマスクされていないnumpy配列に対しては機能しませんでしたstackoverflow.com/q/58675479/2132157
GM

95

Pymplerのパッケージのasizeofモジュールは、これを行うことができます。

次のように使用します。

from pympler import asizeof
asizeof.asizeof(my_object)

とは異なりsys.getsizeof自分で作成したオブジェクトに対して機能します。numpyでも動作します。

>>> asizeof.asizeof(tuple('bcd'))
200
>>> asizeof.asizeof({'foo': 'bar', 'baz': 'bar'})
400
>>> asizeof.asizeof({})
280
>>> asizeof.asizeof({'foo':'bar'})
360
>>> asizeof.asizeof('foo')
40
>>> asizeof.asizeof(Bar())
352
>>> asizeof.asizeof(Bar().__dict__)
280
>>> A = rand(10)
>>> B = rand(10000)
>>> asizeof.asizeof(A)
176
>>> asizeof.asizeof(B)
80096

以下のように述べました

クラス、関数、メソッド、モジュールなどのオブジェクトの(バイト)コードサイズは、optionを設定することで含めることができますcode=True

また、ライブデータに関する他のビューが必要な場合は、Pymplerの

module muppyはPythonアプリケーションのオンライン監視に使用され、モジュールClass Trackerは選択されたPythonオブジェクトの寿命のオフライン分析を提供します。


この関数は、大きなオブジェクトの場合は非常に遅くなります。自分で作成したオブジェクトで機能する「高速」同等のものはありますか?
Shuklaswag 2017年

私はまだテストしていませんが、org.apache.spark.util.SizeEstimator関連があるかもしれません
Shuklaswag

1
@Shuklaswag:Sparkを使用している場合は、そうかもしれません。変換とJavaの見積もりは、Pythonの組み込みメソッドよりも速いと思いますか?または私は誤解しましたか?
serv-inc 2017

3
pympler関数やその他の呼び出し可能オブジェクトおよびコードオブジェクトの実行可能コードサイズを考慮に入れる機能があることに注意してください。
mtraceur 2018年

私が得るTypeError例外を:「『NoneType』オブジェクトが呼び出し可能ではありません」私のカスタムオブジェクトが値との「木」のいくつかのサブオブジェクトを持っている時はいつでもNone。これに対する簡単な回避策はありますか?
James Hirschorn、2018

81

numpy配列でgetsizeofは機能しません-私にとっては、何らかの理由で常に40を返します:

from pylab import *
from sys import getsizeof
A = rand(10)
B = rand(10000)

次に(ipythonの場合):

In [64]: getsizeof(A)
Out[64]: 40

In [65]: getsizeof(B)
Out[65]: 40

幸いにも、:

In [66]: A.nbytes
Out[66]: 80

In [67]: B.nbytes
Out[67]: 80000

29
>すべての組み込みオブジェクトは正しい結果を返しますが、これは実装固有であるため、サードパーティの拡張機能に当てはまる必要はありません。docs.python.org/library/sys.html#sys.getsizeof
warvariuc '30

33
「numpy配列(docs.scipy.org/doc/numpy/reference/arrays.ndarray.html)を使用している場合は、 'ndarray.nbytes'属性を使用してメモリ内のサイズを評価できます。stackoverflow.com/a/15591157/556413
グラレイン

17
40バイトは正しいと思いますが、getsizeof()内部のデータではなく、オブジェクト(配列のヘッダー)のサイズのみを提供します。Pythonのコンテナの同じ場所sys.getsizeof([1,2,4]) == sys.getsizeof([1,123**456,4]) == 48、しばらくsys.getsizeof(123**456) = 436
ヨタ

3
getsizeof()期待値を返すように関数が変更されたようです。
dshin 2017

14

これは、カウント方法に応じて、見た目よりも複雑になる場合があります。たとえば、intのリストがある場合、intへの参照を含むリストのサイズが必要ですか?(つまり、リストに含まれるものではなく、リストのみ)、またはポイントされた実際のデータを含めますか?その場合、重複した参照を処理する必要があり、2つのオブジェクトに参照が含まれている場合の二重カウントを防ぐ方法同じオブジェクト。

pysizerなどのいずれかのpythonメモリプロファイラを調べて、ニーズに合っているかどうかを確認することをお勧めします。


14

ここでレイモンドヘッティンガーが発表したsys.getsizeofように、Python 3.8(2019年第1四半期)では、の結果の一部が変更されます

Pythonコンテナは、64ビットのビルドでは8バイト小さくなります。

tuple ()  48 -> 40       
list  []  64 ->56
set()    224 -> 216
dict  {} 240 -> 232

これは、33597号稲田直樹(methane)によるCompact PyGC_Headに関する作業、およびPR 7043の後に行われます。

このアイデアは、PyGC_Headのサイズを2ワードに減らします

現在、PyGC_Headは3ワードを使用します。gc_prev、、gc_nextおよびgc_refcnt

  • gc_refcnt 収集時に試用削除に使用されます。
  • gc_prev 追跡および追跡解除に使用されます。

だから我々は追跡/ untrackingしばらく試用削除を回避することができ、場合gc_prevgc_refcnt同じメモリ空間を共有することができます。

commit d5c875bを参照してください。

Py_ssize_tから1人のメンバーを削除しましたPyGC_Head
すべてのGC追跡オブジェクト(タプル、リスト、辞書など)のサイズが4バイトまたは8バイト減少します。


10

私は何度もこの問題に遭遇したので、小さな関数(@ aaron-hallの回答に触発されたもの)とsys.getsizeofが期待することを実行するテストを記述しました。

https://github.com/bosswissam/pysize

あなたが裏話に興味があるなら、ここにあります

編集:簡単に参照できるように以下のコードを添付します。最新のコードを確認するには、githubのリンクを確認してください。

    import sys

    def get_size(obj, seen=None):
        """Recursively finds size of objects"""
        size = sys.getsizeof(obj)
        if seen is None:
            seen = set()
        obj_id = id(obj)
        if obj_id in seen:
            return 0
        # Important mark as seen *before* entering recursion to gracefully handle
        # self-referential objects
        seen.add(obj_id)
        if isinstance(obj, dict):
            size += sum([get_size(v, seen) for v in obj.values()])
            size += sum([get_size(k, seen) for k in obj.keys()])
        elif hasattr(obj, '__dict__'):
            size += get_size(obj.__dict__, seen)
        elif hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes, bytearray)):
            size += sum([get_size(i, seen) for i in obj])
        return size

7

これは、すべての変数のサイズをリストするための以前の回答に基づいて私が作成した簡単なスクリプトです

for i in dir():
    print (i, sys.getsizeof(eval(i)) )

それは間違いではなく、あいまいです。sys.getsizeofは常に戻り値が必要であるため、try..exceptでパフォーマンスを失う必要はありません。
der_fenix 2014

ああ、それは良い点であり、私はそれについては考えていませんでした。現在の形式のコードは、それが年代順に書かれた方法を示しているだけです。 。説明をありがとう_ / \ _
alexey

7

オブジェクトをシリアル化して、オブジェクトのサイズに密接に関連するメジャーを取得できます。

import pickle

## let o be the object, whose size you want to measure
size_estimate = len(pickle.dumps(o))

(ラムダ式などの理由で)ピクルできないオブジェクトを測定する場合は、cloudpickleが解決策になります。


4

リンクされた(ネストされた)オブジェクトのサイズを含めたくない場合は、sys.getsizeof()を使用してください。

ただし、リスト、辞書、セット、タプルにネストされたサブオブジェクトを数えたい場合-通常、これはあなたが探しているものです- 以下に示すように、再帰的な深いsizeof()関数を使用します:

import sys
def sizeof(obj):
    size = sys.getsizeof(obj)
    if isinstance(obj, dict): return size + sum(map(sizeof, obj.keys())) + sum(map(sizeof, obj.values()))
    if isinstance(obj, (list, tuple, set, frozenset)): return size + sum(map(sizeof, obj))
    return size

この関数は、他の多くの便利なワンライナーとともに、気の利いたツールボックスにもあります。

https://github.com/mwojnars/nifty/blob/master/util.py


3

オブジェクトの正確なサイズは必要ないが、おおまかにその大きさを知る場合、プログラムを実行させ、長時間スリープさせ、メモリ使用量を確認する(ex :この特定のpythonプロセスによるMacのアクティビティモニター)。これは、Pythonプロセスで1つの大きなオブジェクトのサイズを見つけようとする場合に効果的です。たとえば、最近、新しいデータ構造のメモリ使用量を確認し、それをPythonのセットデータ構造のメモリ使用量と比較したいと考えました。最初に要素(大きなパブリックドメインの本の単語)をセットに書き込み、次にプロセスのサイズを確認し、次に他のデータ構造で同じことを行いました。セットを使用したPythonプロセスは、新しいデータ構造の2倍のメモリを使用していることがわかりました。繰り返しますが、プロセスが使用するメモリはオブジェクトのサイズと同じであると正確に言えます。オブジェクトのサイズが大きくなると、監視しようとしているオブジェクトのサイズと比較して、残りのプロセスによって消費されるメモリが無視できるようになるため、これは近くなります。


1
質問は Pythonオブジェクトのメモリ使用量見つけるだけでなく、Pythonでそれを行う方法を尋ねます。Macのアクティビティモニターまたは他の同様のソフトウェアを使用して、プログラムでPythonを使用していません。ことでは...この方法でのpythonのプロセスのメモリ使用量をチェックすることは、一般的には何もなくなって間違っていないことを確認するための良い方法である、と述べた
トムWyllie

@TomWyllie、ありがとうございます。ただし、この回答に反対票を投じると、回答自体が間違っていて何も達成されないという否定的な意味合いが生じます。私が言及するメソッドはPythonで実装されていない可能性がありますが、これはPythonオブジェクトのサイズの概算を取得するための便利な方法です。私は正確な質問に答えていないことを知っていましたが、この方法は他の誰かが同様の結果を得るために役立つ可能性があります。
picmate涅

1

下記のようにgetSizeof()を使用してオブジェクトのサイズを決定できます

import sys
str1 = "one"
int_element=5
print("Memory size of '"+str1+"' = "+str(sys.getsizeof(str1))+ " bytes")
print("Memory size of '"+ str(int_element)+"' = "+str(sys.getsizeof(int_element))+ " bytes")

0

私はこのトリックを使用しています...小さなオブジェクトでは正確でない可能性がありますが、sys.getsizeof()よりも、pygameサーフェスなどの複雑なオブジェクトの方がはるかに正確だと思います

import pygame as pg
import os
import psutil
import time


process = psutil.Process(os.getpid())
pg.init()    
vocab = ['hello', 'me', 'you', 'she', 'he', 'they', 'we',
         'should', 'why?', 'necessarily', 'do', 'that']

font = pg.font.SysFont("monospace", 100, True)

dct = {}

newMem = process.memory_info().rss  # don't mind this line
Str = f'store ' + f'Nothing \tsurface use about '.expandtabs(15) + \
      f'0\t bytes'.expandtabs(9)  # don't mind this assignment too

usedMem = process.memory_info().rss

for word in vocab:
    dct[word] = font.render(word, True, pg.Color("#000000"))

    time.sleep(0.1)  # wait a moment

    # get total used memory of this script:
    newMem = process.memory_info().rss
    Str = f'store ' + f'{word}\tsurface use about '.expandtabs(15) + \
          f'{newMem - usedMem}\t bytes'.expandtabs(9)

    print(Str)
    usedMem = newMem

私のWindows 10、Python 3.7.3では、出力は次のようになります。

store hello          surface use about 225280    bytes
store me             surface use about 61440     bytes
store you            surface use about 94208     bytes
store she            surface use about 81920     bytes
store he             surface use about 53248     bytes
store they           surface use about 114688    bytes
store we             surface use about 57344     bytes
store should         surface use about 172032    bytes
store why?           surface use about 110592    bytes
store necessarily    surface use about 311296    bytes
store do             surface use about 57344     bytes
store that           surface use about 110592    bytes
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.