web-dev-qa-db-ja.com

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

Cでは、intcharなどのサイズを見つけることができます。Pythonで文字列、整数などのオブジェクトのサイズを取得する方法を知りたいです。

関連する質問: Pythonリストには1要素あたり何バイトありますか(Tuple)?

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

526
user46646

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

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

あなたが2.6未満のpythonを使っていてsys.getsizeofを持っていないのなら、代わりに この広範囲なモジュール を使うことができます。それを使ったことがない。

530
nosklo

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

「sys.getsizeofだけを使う」という答えは完全な答えではありません。

その答えはdoesは組み込みオブジェクトに対して直接働きますが、それらのオブジェクトに何が含まれているのか、特にカスタムオブジェクト、タプル、リスト、辞書、セットのような型には含まれません。それらは相互にインスタンスを含むことができ、数字、文字列、その他のオブジェクトも含むことができます。

より完全な答え

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バイトに1倍になったため、セット自体は736です。それから項目のサイズを追加するので、合計1736バイトになります。

関数とクラスの定義に関するいくつかの注意点

各クラス定義には、クラスattrsのプロキシ__dict__(48バイト)構造があります。各スロットはクラス定義内に(propertyのような)ディスクリプタを持ちます。

スロット付きインスタンスは、最初の要素の48バイトから始まり、さらに8ずつ増えます。空のスロット付きオブジェクトだけが16バイトを持ち、データのないインスタンスはほとんど意味がありません。

また、それぞれの関数定義はコードオブジェクト、たぶんdocstrings、そして他の可能な属性、__dict__さえも持っています。

guppy.hpyおよびsys.getsizeofで確認されたPython 2.7の解析

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では、辞書( は設定されていません )は よりコンパクトな表現になっています

64ビットマシンでは、参照する追加項目ごとに8バイトを使用するのが理にかなっていると思います。これらの8バイトは、含まれている項目があるメモリ内の場所を指しています。私が正しく思い出した場合、Python 2ではUnicodeの4バイトが固定幅ですが、Python 3では、strは文字の最大幅に等しいwidthのUnicodeになります。

(そして、スロットの詳細については、 この回答を参照してください

より充実した機能

リスト、タプル、セット、辞書、obj.__dict__obj.__slots__の要素を検索する関数が欲しいのですが、まだ考えていないかもしれません。

これはCレベルで動作するため(非常に高速になるため)、この検索を行うにはgc.get_referentsを信頼したいと思います。欠点は、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モジュールに頼るのではなく、この再帰関数を作成して、ほとんどの組み込み型、collectionsモジュールの型、カスタム型(スロット型など)を含むほとんどの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回しか存在しないため、サイズはそれほど重要ではありません。

250
Aaron Hall

ぎこちない配列の場合、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
77
Mike Dewar

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

次のように使用してください。

from pympler import asizeof
asizeof.asizeof(my_object)

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

>>> 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

が述べたように

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

ライブデータについて他の見方が必要なら、Pympler's

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

58
serv-inc

これは、物事の数え方によっては見た目より複雑になることがあります。例えば、あなたが整数のリストを持っているなら、あなたは整数の 参照 を含むリストのサイズが欲しいですか? (リストに含まれているものではなく、リストされているものではありません)または指し示す実際のデータを含めますか。同じオブジェクト.

pysizer のようなpythonメモリプロファイラを見て、あなたのニーズを満たしているかどうかを確かめてください。

12
Brian

これは、すべての変数のリストサイズに対する以前の回答に基づいて書いた簡単なスクリプトです。

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

私はこの問題に何度も遭遇したので、(@ 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
wissam

Python 3.8(Q1 2019)は、いくつかの sys.getsizeof の結果を、Raymond Hettingerによる ここで発表 に変更します。

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

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

これは、 issue 33597 および Inada Naoki(methane がCompact PyGC_Headを回避し、 PR 7043 の後にくる

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

現在、PyGC_Headは 3つの単語を取ります ; gc_prevgc_next、およびgc_refcnt

  • gc_refcntは、トライアル削除のために収集時に使用されます。
  • gc_prevは追跡と追跡解除に使用されます。

したがって、試し削除中に追跡/追跡解除を回避できれば、gc_prevgc_refcntは同じメモリ空間を共有できます。

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

Py_ssize_tから1つのPyGC_Headメンバーを削除しました。
GCで追跡されているすべてのオブジェクト(Tuple、list、dictなど)のサイズが4バイトまたは8バイトに減少しました。

3
VonC

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

0
picmate 涅