世界を動かす技術を、日本語で。

プログラマーが知っておくべきPythonの数値

2026年1月1日原文(mkennedy.codes)

概要

  • Pythonで知っておくべき 基本的な性能指標 のまとめ
  • 各種データ構造や操作の 速度・メモリコスト を比較
  • Webフレームワークやシリアライズ のパフォーマンスも網羅
  • ベンチマーク環境 の詳細も記載
  • 実践的な選択基準 や最適化のヒントを提示

Pythonプログラマが知っておくべき数値一覧

  • Pythonの各操作やデータ構造 のパフォーマンスを一覧化
  • 速度・メモリ消費量 をカテゴリごとに整理
  • ベンチマーク環境 :CPython 3.14.2、Mac Mini M4 Pro(ARM、14コア、24GB RAM、macOS Tahoe)
  • 相対比較 が重要:自分の環境との差よりも、どの操作が速い・遅いかを重視
  • ベンチマークコード はGitHubで公開(python-numbers-everyone-should-know

メモリ消費量

  • 空のPythonプロセス :15.73MB
  • 文字列
    • 空文字列:41バイト
    • 100文字:141バイト
  • 数値
    • 小さなint(0〜256):28バイト
    • float:24バイト
  • リスト・辞書・セット
    • 空リスト:56バイト
    • 1,000個のintリスト:35.2KB
    • 空辞書:64バイト
    • 1,000要素辞書:63.4KB
    • 空セット:216バイト
    • 1,000要素セット:59.6KB
  • クラスインスタンス
    • 通常クラス(5属性):694バイト
    • __slots__クラス(5属性):212バイト
    • namedtuple:228バイト
    • dataclass:694バイト

基本操作の速度

  • 整数の加算 :19.0ns(1秒間に5,270万回)
  • 浮動小数点加算 :18.4ns
  • 文字列連結(+) :39.1ns
  • f-string :64.9ns
  • .format() :103ns
  • リストappend :28.7ns
  • リスト内包表記(1,000件) :9.45μs
  • forループ(1,000件) :11.9μs

コレクション操作

  • 辞書キー参照 :21.9ns
  • セット会員判定 :19.0ns
  • リストインデックスアクセス :17.6ns
  • リストin判定(1,000件) :3.85μs
  • len() :18.8ns(リスト1,000件)
  • リスト走査(1,000件) :7.87μs
  • 辞書走査(1,000件) :8.74μs

属性アクセス

  • 通常クラス属性読み :14.1ns
  • __slots__クラス属性読み :14.1ns
  • @property読み :19.0ns
  • getattr() :13.8ns
  • hasattr() :23.8ns

JSON・シリアライズ

  • json.dumps()(単純) :708ns
  • json.loads()(単純) :714ns
  • orjson.dumps()(複雑) :310ns
  • ujson.dumps()(複雑) :1.64μs
  • msgspec encode(複雑) :445ns
  • Pydantic model_dump_json() :1.54μs

Webフレームワーク

  • Flask(JSON返却) :16.5μs
  • Django(JSON返却) :18.1μs
  • FastAPI(JSON返却) :8.63μs
  • Starlette(JSON返却) :8.01μs
  • Litestar(JSON返却) :8.19μs

ファイルI/O

  • ファイルオープン・クローズ :9.05μs
  • 1KBファイル読み :10.0μs
  • 1KBファイル書き :35.1μs
  • 1MBファイル書き :207μs
  • pickle.dumps() :1.30μs
  • pickle.loads() :1.44μs

データベース操作

  • SQLite insert(JSON blob) :192μs
  • SQLite select by PK :3.57μs
  • diskcache set :23.9μs
  • MongoDB insert_one :119μs
  • MongoDB find_one by _id :121μs

関数呼び出し

  • 空関数呼び出し :22.4ns
  • 引数5つの関数 :24.0ns
  • メソッド呼び出し :23.3ns
  • ラムダ呼び出し :19.7ns
  • try/except(例外なし) :21.5ns
  • try/except(例外あり) :139ns
  • isinstance()チェック :18.3ns

非同期処理

  • コルーチン生成 :47.0ns
  • run_until_complete(empty) :27.6μs
  • asyncio.sleep(0) :39.4μs
  • gather() 10コルーチン :55.0μs
  • create_task() + await :52.8μs
  • async with(コンテキストマネージャ) :29.5μs

メモリコスト詳細

  • 文字列
    • 空文字列:41バイト
    • 1文字追加ごとに+1バイト
    • 100文字:141バイト
  • 数値
    • intは28バイト(小・大問わず)
    • 非常に大きなint(10100):72バイト
    • float:24バイト
  • コレクション
    • リスト、辞書、セットは空でも数十〜数百バイト
    • 1,000要素で数十KBに増加
  • クラスインスタンス
    • __slots__を使うと、通常クラスの約1/3のメモリ消費
    • 大量のインスタンスを扱う場合は__slots__推奨

基本操作・コレクション操作の比較

  • Pythonの演算はC/C++より遅いが、十分高速
  • f-stringが最速の文字列フォーマット
  • リスト内包表記はforループ+appendより26%高速
  • 辞書・セットの検索はリストの200倍高速(1,000件時)
  • len()は最適化不要なレベルの高速

属性アクセス・クラス設計

  • __slots__導入でメモリ大幅削減
  • 属性アクセス速度は通常クラスとほぼ同等
  • namedtupleやdataclassは柔軟性とメモリ効率のバランスが良い

JSON・シリアライズ・Webフレームワーク

  • orjsonやmsgspecは標準jsonより8倍以上高速
  • FastAPIやStarletteはDjango/Flaskより2倍近く高速
  • Webフレームワーク間の違いは、単純なJSON返却のみで測定

ファイルI/O・データベース

  • ファイルI/Oはμsオーダー、1MB書き込みでも0.2ms程度
  • SQLiteやMongoDBの基本操作もμs〜msで完了

まとめ・最適化のヒント

  • パフォーマンスクリティカルな場面では、適切なデータ構造選択が重要
  • 大量のインスタンスを扱う場合、__slots__やnamedtupleの活用が有効
  • Web APIではFastAPIやStarletteのような軽量フレームワークが高効率
  • シリアライズにはorjsonやmsgspecなどの高速ライブラリを検討
  • ベンチマークの数値は絶対値より相対的な差に注目

参考文献・リンク

Hackerたちの意見

逆に言うと、これらの数字を知らなくてもやっていけるなら、Pythonでプログラムを書くべきだよ。これが重要になってくると、Pythonはその仕事に合ったツールじゃなくなる。

その通り。これらの数字が重要なアプリケーションに取り組んでいるなら、Pythonは最適化するには高すぎるレベルの言語だよ。

それか、Pythonのスキャフォールディングを保ちながら、パフォーマンスが重要な部分をCやRustの拡張に押し込むのもありだよ。numpyやpandas、PyTorchなんかがそうしてるしね。でも、君が書いたことの精神には同意するよ。これらの数字は面白いけど、覚える価値はない。むしろ、実際のユーザーデータでどこが遅いのかを見つけるために、プロダクションでコードを計測する方が大事だよ(早すぎる最適化はすべての悪の根源とか言うし)。コードをプロファイリングして(pyspyが一番いいツールだよ、CPUを食うコードを探すのに)、もしPythonでリストに何かを追加するのにどれくらい時間がかかるかを心配しているなら、その操作はPythonでやるべきじゃないよ。

同意だな。Pythonを20年使ってきたけど、これらの数字を知る必要はなかったし、今の仕事でも必要ない。タイトルとは逆にね。パフォーマンス最適化のためにプロファイリングを定期的に使ってるし、必要に応じてCythonやSWIG、JITライブラリ、他のツールを選んでる。これらの数字は、私の意思決定には全く関係ないよ。

なんで?私はturbodbcとpandasを使って、Pythonでかなり大規模な分析データフローを構築したけど、基本的にC++並みに速いよ。メモリを多く使うから君の言う通りだけど、その反面、年間の追加コストは5〜10ドルの話だよ。正直、年間2万ドルかかっても、私みたいな人をもっと雇うよりは安いし、数人でやってBIの人たちに提供するツールを使わせる方がいい。埋め込み作業をする時も、マイクロPythonはエンジニアスタッフにとって扱いやすいんだ。CとPythonの相互運用性が素晴らしいし、Pythonの数字を知ることで、実際にCで何かを作るべきタイミングがわかる。Zigが本当に素晴らしい相互運用性を持ってきてるから、状況は今まで以上に良くなってるよ。君が間違ってるわけじゃないけどね。飛行機を動かすのにPythonは使わないけど、インタープリター言語やGC言語を使ってるからって、リソースに気を使わない理由はないと思うよ。

これらは基本的に最後の手段の数字に見える。プロファイリングして、通常の犯人(大きなディスク読み込み、ネットワークレイテンシー、多項式や指数時間アルゴリズム、無駄に過剰なデータ構造など)を排除した後、個々の操作のレベルで最適化が必要なときに使う数字だね。

空の文字列がどれくらいメモリを使うかを知っても、あんまり得るものはないと思う。この記事や挙げられている数字は、メモリ使用量や具体的な時間測定に異常にこだわってる。プログラマーにとって本当に重要なのは、時間と空間の複雑さだよ。無駄に遅いプログラムやメモリを食うプログラムを設計しないためにね。Pythonを使う前提で、整数が28バイト取ることを知って何の役に立つの?結局、自分が書いたプログラムがパフォーマンス基準を満たしているかどうかを判断しなきゃいけないし、満たしてないなら、もっと賢いアルゴリズムやデータ処理の方法が必要になる。1000x1000のboolの2D配列がどれくらい大きいかを知っても、あんまり役に立たないよ。大事なのは、それが多すぎるのか、もしかしたら大きな整数やビットボードアプローチに切り替えた方がいいのかを知ることだね。あるいは言語を変えるとか。

Pythonを使う前提で、整数が28バイト取ることを知って何の役に立つの? 大量のオブジェクトを生成する必要がある場合は関係あるよ。これを聞いて、エリック・レイモンドがGCCを移行するためにReposurgeonを使おうとして直面した問題について語っていた投稿を思い出した。http://esr.ibiblio.org/?p=8161

俺は違うと思う。パフォーマンスは常に重要な漏れやすい抽象だよ。それに対する認識は暗黙的か明示的かのどちらか。例えば、リストの追加が線形で、二次的ではなくて、かなり速いってことを知らなかったとしても。単純なプログラムが何らかの理由で必要以上に10000倍遅くても気にしないってことがあったとしても、それが十分なレベルに達しているからとか、問題の非効率が自分に影響しないからとか。自分より下のライブラリの著者はそれを知っているし、あなたが使うAPIや、見るPythonicなコード、LLMSが生成するコードはその漏れやすい抽象の影響を受けるんだ。もしn^2の単純なリスト追加が悪い例だと思うなら、それは違うよ。Pythonの文字列追加はn^2で、それが人々のやり方に影響を与えている。例えば、f文字列は遅延評価だし。Pythonで辞書が速いっていうのは、文字通りどこでも使われる理由でもある。Raymondの古いPycon 2017のトークがこれについて話してる。結局、ブログの著者が提供したのは、パフォーマンスの理解が与える暗黙的な知識に対する数値的な根拠なんだ。

すべてのPythonプログラマーは、低レベルのパフォーマンスの細かいことよりも、もっと重要なことを考えるべきだよ。素晴らしい参考だけど、最適化が必要な稀なケースを除いて、実際にはあんまり関係ないよ。もしワークロードがこのことが実際に重要になるほど増えたら、素晴らしい!それまでは気を散らすだけだね。

そうだね、限界に達したら、Cで実装されたモジュールを探すか、自分で書けばいいよ。これがPythonでいつもやられてきた方法だよ。

同意だな。ただ、これはここ数年ずっと感じてたことなんだ。物事は十分早くて大丈夫だし。このページは、数字を使ってその事実を思い出させてくれるいいリマインダーだね。少なくともしばらくの間は、低レベルのパフォーマンスの細かいことを無視できるって、感じるだけじゃなくて知ることができる。

Hacker Newsで議論の続きを見る