889

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

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

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

4

15 に答える 15

823

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

sys.getsizeof(object[, default])

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

オブジェクトに直接起因するメモリ消費のみが考慮され、参照するオブジェクトのメモリ消費は考慮されません。

引数を使用defaultすると、オブジェクトタイプがサイズを取得する手段を提供せず、を引き起こす場合に返される値を定義できます TypeError

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

コンテナのサイズとそのすべての内容を再帰的に使用する例については、recursivesizeofレシピを参照してください。getsizeof()

使用例、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を使用できます。しかし、それを使用したことはありません。

于 2009-01-16T10:42:37.743 に答える
516

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

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

その答え組み込みオブジェクトに対して直接機能しますが、それらのオブジェクトに含まれる可能性があるもの、具体的には、カスタム オブジェクト、タプル、リスト、ディクテーション、セットなどの型が含まれているかどうかは考慮されていません。それらには、数値、文字列、その他のオブジェクトだけでなく、インスタンス同士を含めることができます。

より完全な答え

Anaconda ディストリビューションの 64 ビット Python 3.6 を使用してsys.getsizeof、次のオブジェクトの最小サイズを決定しました。set と dict はスペースを事前に割り当てるため、空のオブジェクトは一定量 (言語の実装):

パイソン 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 バイトを持ち、データのないインスタンスはほとんど意味がありません。

また、各関数定義には、コード オブジェクト (docstring など) やその他の可能な属性 ( __dict__.

docs から、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 では、辞書 ( set ではない) の表現がよりコンパクトになったことに注意してください。

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

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

より完全な機能

リスト、タプル、セット、dicts、obj.__dict__'s、および の要素を検索する関数と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 deque
from collections.abc import Set, Mapping


ZERO_DEPTH_BASES = (str, bytes, Number, range, bytearray)


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, 'items'):
            size += sum(inner(k) + inner(v) for k, v in getattr(obj, 'items')())
        # 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

この実装は、クラス定義と関数定義のすべての属性を追跡するわけではないため、分解されますが、それらはプロセスのメモリ内に一度だけ存在する必要があるため、それらのサイズは実際にはあまり重要ではありません。

于 2015-05-19T04:26:33.410 に答える
141

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 の

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

于 2015-11-10T14:01:35.260 に答える
84

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
于 2010-07-30T16:33:00.823 に答える
25

Python 3.8 (2019 年第 1 四半期)では、Raymond Hettingerがここで発表したsys.getsizeofように、 の結果の一部が変更されます。

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

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

これはissue 33597Inada 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.

コミット d5c875bを参照してください。

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

于 2019-02-19T17:04:22.720 に答える
14

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

pysizerなどの Python メモリ プロファイラーの 1 つを調べて、それらがニーズを満たしているかどうかを確認することをお勧めします。

于 2009-01-16T13:00:14.293 に答える
7

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

for i in dir():
    print (i, sys.getsizeof(eval(i)) )
于 2013-03-04T17:34:04.850 に答える
5

次の関数を使用して、Python オブジェクトの実際のサイズを取得します。

import sys
import gc

def actualsize(input_obj):
    memory_size = 0
    ids = set()
    objects = [input_obj]
    while objects:
        new = []
        for obj in objects:
            if id(obj) not in ids:
                ids.add(id(obj))
                memory_size += sys.getsizeof(obj)
                new.append(obj)
        objects = gc.get_referents(*new)
    return memory_size

actualsize([1, 2, [3, 4, 5, 1]])

参照: https://towardsdatascience.com/the-strange-size-of-python-objects-in-memory-ce87bdfbb97f

于 2021-07-31T09:45:08.500 に答える