[解決済み] なぜタプルはリストよりも少ないメモリ容量で済むのですか?
質問
A
tuple
はPythonではより少ないメモリスペースで動作します。
>>> a = (1,2,3)
>>> a.__sizeof__()
48
ここで
list
はより多くのメモリ空間を必要とします。
>>> b = [1,2,3]
>>> b.__sizeof__()
64
Pythonのメモリ管理は内部でどうなっているのでしょうか?
どのように解決するのですか?
CPythonを64ビットで使用しているのでしょう(私はCPython 2.7 64ビットで同じ結果を得ました)。他の Python の実装や、32 ビットの Python を使用している場合は、違いがある可能性があります。
実装に関係なく
list
は可変サイズであるのに対し
tuple
は固定サイズです。
そのため
tuple
は構造体の中に直接要素を格納できますが、リストは間接的なレイヤーを必要とします(要素へのポインタを格納します)。この間接参照層はポインターで、64ビットシステムでは64ビット、つまり8バイトになります。
しかし、もうひとつ
list
が行うもうひとつのこと、それは過剰なアロケーションです。そうでなければ
list.append
は
O(n)
操作
常に
- 償却させるために
O(1)
(ずっと速い!!) にするために、過剰に割り当てるのです。しかし、今度は
が割り当てられた
のサイズと
で満たされている
サイズ (
tuple
は1つのサイズしか保存する必要がありません。なぜなら、割り当てられたサイズと充填されたサイズは常に同一だからです。) つまり、各リストは別の "size" を格納する必要があり、これは 64 ビット システムでは 64 ビットの整数で、再び 8 バイトになります。
そこで
list
よりも少なくとも 16 バイト多いメモリが必要です。
tuple
s. なぜ私は「少なくとも」と言ったのでしょうか?それは、オーバーアロケーションがあるからです。オーバーアロケーションとは、必要以上のスペースを割り当てるということです。ただし、オーバーアロケーションの量は、リストの作成方法と追加/削除の履歴に依存します。
>>> l = [1,2,3]
>>> l.__sizeof__()
64
>>> l.append(4) # triggers re-allocation (with over-allocation), because the original list is full
>>> l.__sizeof__()
96
>>> l = []
>>> l.__sizeof__()
40
>>> l.append(1) # re-allocation with over-allocation
>>> l.__sizeof__()
72
>>> l.append(2) # no re-alloc
>>> l.append(3) # no re-alloc
>>> l.__sizeof__()
72
>>> l.append(4) # still has room, so no over-allocation needed (yet)
>>> l.__sizeof__()
72
画像
上記の説明に付随して、いくつかの画像を作成することにしました。たぶん、これらは役に立つでしょう
あなたの例では、このように(模式的に)メモリに格納されています。違いを赤の(フリーハンドの)サイクルで強調しました。
これは実際には単なる近似値です。
int
オブジェクトは Python のオブジェクトでもあり、CPython は小さな整数さえも再利用するので、メモリ上のオブジェクトをおそらくより正確に表現すると(読みやすくはありませんが)、次のようになります。
便利なリンク集です。
-
tuple
Python 2.7 用の CPython リポジトリにある構造体です。 -
list
Python 2.7 用の CPython リポジトリにある構造体 -
int
Python 2.7 用の CPython リポジトリにある構造体
注意点として
__sizeof__
は実際には "正しい" サイズを返しません! これは、格納されている値のサイズを返すだけです。しかし
sys.getsizeof
を使用すると、結果は異なります。
>>> import sys
>>> l = [1,2,3]
>>> t = (1, 2, 3)
>>> sys.getsizeof(l)
88
>>> sys.getsizeof(t)
72
24 バイトの "extra" があります。これらは
本当の
で考慮されていないガベージコレクタのオーバーヘッドです。
__sizeof__
メソッドでは考慮されないガベージコレクタのオーバーヘッドです。それは、一般的にマジックメソッドを直接使用することは想定されていないからです。
sys.getsizeof
(これは実際には
は GC オーバーヘッドを追加します。
から返される値に
__sizeof__
).
関連
-
[解決済み] 最小限の驚き」と「変更可能なデフォルトの引数
-
[解決済み] Python 3で「1000000000000000 in range(1000000000000001)」はなぜ速いのですか?
-
[解決済み] モジュールの関数名(文字列)を使って、モジュールの関数を呼び出す。
-
[解決済み] Pythonスクリプトのプロファイリングはどのように行うのですか?
-
[解決済み] リストとタプルの違いは何ですか?
-
[解決済み] リストに値が存在するかどうかを確認する最速の方法
-
[解決済み] Pythonの「名前付きタプル」とは何ですか?
-
[解決済み] なぜ[]はlist()よりも速いのですか?
-
[解決済み] Pythonでコード行間にかかる時間を測定するには?
-
[解決済み] Pandasを使って、既存のExcelファイルに新しいシートを保存する方法は?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Jupyterノートブックでenv変数を設定する方法
-
[解決済み] SQLAlchemy: セッションの作成と再利用
-
[解決済み] googletransがエラー 'NoneType' オブジェクトに 'group' 属性がない、と言って動かなくなった。
-
[解決済み] Pythonの要素別タプル演算(sumなど
-
[解決済み] 文字列のリストを内容に基づいてフィルタリングする
-
[解決済み] サブフォルダからのインポートモジュール
-
[解決済み] Python Logging でログメッセージが2回表示される件
-
[解決済み] CSVデータを処理する際、1行目のデータを無視する方法を教えてください。
-
[解決済み] Flaskで非同期タスクを作る
-
[解決済み] Pythonでリストが空かどうかをチェックする方法は?重複