Cでは、int
、char
などのサイズを見つけることができます。Pythonで文字列、整数などのオブジェクトのサイズを取得する方法を知りたいです。
関連する質問: Pythonリストには1要素あたり何バイトありますか(Tuple)?
値のサイズを指定するサイズフィールドを含むXMLファイルを使用しています。このXMLを解析してコーディングをしなければなりません。特定のフィールドの値を変更したい場合は、その値のサイズフィールドを確認します。ここで、入力しようとしている新しい値がXMLと同じサイズであるかどうかを比較します。新しい値のサイズを確認する必要があります。文字列の場合はその長さと言えます。しかしint、floatなどの場合は混乱します。
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
を持っていないのなら、代わりに この広範囲なモジュール を使うことができます。それを使ったことがない。
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回しか存在しないため、サイズはそれほど重要ではありません。
ぎこちない配列の場合、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
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アプリケーションのオンライン監視に使用され、moduleClass Tracker
は選択されたPythonオブジェクトの寿命のオフライン分析を提供します。
これは、物事の数え方によっては見た目より複雑になることがあります。例えば、あなたが整数のリストを持っているなら、あなたは整数の 参照 を含むリストのサイズが欲しいですか? (リストに含まれているものではなく、リストされているものではありません)または指し示す実際のデータを含めますか。同じオブジェクト.
pysizer のようなpythonメモリプロファイラを見て、あなたのニーズを満たしているかどうかを確かめてください。
これは、すべての変数のリストサイズに対する以前の回答に基づいて書いた簡単なスクリプトです。
for i in dir():
print (i, sys.getsizeof(eval(i)) )
私はこの問題に何度も遭遇したので、(@ 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
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_prev
、gc_next
、およびgc_refcnt
。
gc_refcnt
は、トライアル削除のために収集時に使用されます。gc_prev
は追跡と追跡解除に使用されます。したがって、試し削除中に追跡/追跡解除を回避できれば、
gc_prev
とgc_refcnt
は同じメモリ空間を共有できます。
commit d5c875b :を参照してください。
Py_ssize_t
から1つのPyGC_Head
メンバーを削除しました。
GCで追跡されているすべてのオブジェクト(Tuple、list、dictなど)のサイズが4バイトまたは8バイトに減少しました。
あなたがオブジェクトの正確なサイズを必要としないが、それがどれくらい大きいかを大まかに知るために、1つの迅速な(そして汚い)方法はプログラムを実行させ、長期間スリープさせそしてメモリ使用量をチェックすることです:この特定のpythonプロセスによるMacのアクティビティモニタ)。これは、pythonプロセスで単一のラージオブジェクトのサイズを見つけようとしているときに効果的です。例えば、私は最近、新しいデータ構造のメモリ使用量をチェックし、それをPythonの集合データ構造のそれと比較したいと思いました。最初に要素(大きなパブリックドメインの本からの単語)をセットに書き、それからプロセスのサイズをチェックし、そして他のデータ構造で同じことをしました。私は、Pythonのプロセスが新しいデータ構造の2倍のメモリを消費していることを発見しました。繰り返しますが、プロセスによって使用されるメモリがオブジェクトのサイズと等しいとは言えません。オブジェクトのサイズが大きくなるにつれて、残りのプロセスによって消費されるメモリが、監視しようとしているオブジェクトのサイズと比較して無視できるほど小さくなるため、これは近くなります。