Python sys.getsizeof()とは?オブジェクトのメモリサイズを調べる方法
Pythonのsys.getsizeof()は、オブジェクトがメモリ上で占めるバイト数を返す関数です。
ただし「中身まで含めた合計」ではないという重要な制約があります。ここを誤解すると、まったく違う数字を見ることになります。
sys.getsizeof()の基本的な使い方
import sys
for v in [0, 1, "", "a", [], [1, 2, 3], {}, None]:
print(f"{repr(v):12} {sys.getsizeof(v):>4} バイト")
実行結果(64ビット環境の例):
0 24 バイト
1 28 バイト
'' 49 バイト
'a' 50 バイト
[] 56 バイト
[1, 2, 3] 120 バイト
{} 64 バイト
None 16 バイト
数値がやや大きく感じられます。Pythonではすべてがオブジェクトで、型情報や参照カウントなどの管理データを持つためです。
整数の0でさえ24バイト——C言語のintが4バイトであることを考えると、その差が分かります。
最大の注意点|中身は数えられない
import sys
big = [list(range(1000))]
print("外側のリスト:", sys.getsizeof(big))
実行結果:
外側のリスト: 64
1000個の要素が入っているのにたった64バイトです。
リストが持っているのは中身への「参照」だけで、実体は別の場所にあります。getsizeof()は参照の分しか数えません。
import sys
inner = [1, 2, 3]
outer = [inner]
print("outer:", sys.getsizeof(outer))
print("inner:", sys.getsizeof(inner))
実行結果:
outer: 64
inner: 120
外側より中身のほうが大きい——「浅い」計測だと理解していないと必ず混乱します。
中身も含めて数えるには
import sys
big = [list(range(1000))]
total = sys.getsizeof(big) + sum(sys.getsizeof(x) for x in big)
print("中身を足すと:", total)
実行結果:
中身を足すと: 8120
64バイトから8120バイトになりました。127倍の違いです。
ただしこれも完全ではありません。さらに中の整数まで数えるなら再帰が必要で、同じオブジェクトを複数から参照している場合は二重に数えてしまいます。
正確なメモリ使用量を知りたいなら、tracemalloc(標準ライブラリ)やmemory_profilerを使ってください。getsizeof()は単体のオブジェクトを比較する道具と割り切るのが正解です。
リストは少しずつ余分に確保する
import sys
l = []
prev = sys.getsizeof(l)
print("0要素:", prev)
for i in range(1, 9):
l.append(i)
s = sys.getsizeof(l)
if s != prev:
print(f"{i}要素: {s}(増えた)")
prev = s
実行結果:
0要素: 56
1要素: 88(増えた)
5要素: 120(増えた)
1要素ごとにサイズが増えるわけではありません。まとめて確保しておき、足りなくなったら広げるという動きです。
毎回確保し直すと遅くなるための工夫で、append()が高速な理由でもあります。
型による違いを比べる
import sys
data = list(range(1000))
print("list :", sys.getsizeof(data))
print("tuple:", sys.getsizeof(tuple(data)))
print("set :", sys.getsizeof(set(data)))
実行結果:
list : 8056
tuple: 8040
set : 32984
同じ1000件でも、型によって必要なメモリが変わります。setはlistの4倍以上です。
| 型 | 特徴 |
|---|---|
tuple |
変更しない前提なので余分な確保がない |
list |
追加に備えて余分に確保する |
set・dict |
高速な検索のためすき間を持つ |
「変更しないならタプル」と言われる理由の1つが、このメモリ効率です。
ジェネレータとの比較
import sys
print("リスト :", sys.getsizeof([x * 2 for x in range(10000)]))
print("ジェネレータ:", sys.getsizeof((x * 2 for x in range(10000))))
実行結果:
リスト : 85176
ジェネレータ: 112
ジェネレータは値を持たず作り方だけを覚えているため、件数が増えてもサイズが変わりません。
ここはgetsizeof()の「浅い」性質がむしろ実態を正しく表している珍しい例です。大量データを扱うときにジェネレータが推奨される理由が、数字で確認できます。
__sizeof__を持たない型もある
import sys
sys.getsizeof(obj, 0) # 第2引数は既定値
第2引数を渡しておくと、サイズを取得できない型でもTypeErrorにならず既定値が返ります。
NumPyの配列など、独自の管理をしている型ではgetsizeof()が実態と合わないことがあります。NumPyならarr.nbytesを使ってください。
sys.getsizeof()のまとめ
getsizeof()はオブジェクト単体のバイト数を返す- 中身は数えない——リストの中の要素は含まれない
- 合計が欲しいなら自分で足す(完全ではない)
- リストは余分に確保するので、要素ごとに増えるわけではない
- tuple は list より省メモリ、set・dict はすき間を持つ
- 正確な計測は
tracemalloc・memory_profilerへ



