この記事は 3.8〜3.15 を横断して比較したうえで、個人的に 3.11 と 3.13 をおすすめする理由を書いている。「最新が正義」でも「安定が正義」でもなく、選択の根拠を整理したくて書いた。ちなみに個人的に一番好きだったのは Python 3.9。ただし、好きだったバージョンと今おすすめするバージョンは別の話。あくまで個人的な見解なので、異論は歓迎。
※今後も新しいバージョンが登場した際には、内容を更新する予定です。
サポートスケジュールの全体像
選択の前提として、まずどのバージョンがいつまで生きているかを把握しておく。
3.8 は 2024年10月、3.9 は 2025年10月、3.10 は 2026年10月1日にそれぞれ EOL を迎えている。セキュリティパッチも届かないため、新規プロジェクトへの採用は推奨できない。既存プロジェクトを維持しているケースでは話が別だが、移行計画は立てておいた方がいい。
パフォーマンスの変遷
Python 3.11 はインタープリタの高速化が大きく進んだ転換点だった。ただし、処理内容や実行環境によって結果は変わるため、バージョンごとの速度を一つの数値で順位付けすることは難しい。
3.15 では実験的 JIT が改善され、Python公式は x86-64 Linux 上で、通常のインタープリタに対して幾何平均約7〜8%の高速化を報告している。これは 3.9 や 3.14 に対する一律の性能差ではない。
元記事の「3.9=100」の比較表は、同じ条件で測定した一次データを確認できなかったため、今回の更新ではいったん掲載を見送った。
出典:https://www.python.org/downloads/release/python-3150/
バージョン別の主要変更点
3.8(2019年10月)
# セイウチ演算子(:=)— 条件式の中で代入できる
import re
data = "Error: Connection refused"
if m := re.search(r"Error: (.+)", data):
print(m.group(1)) # Connection refused
# 位置専用引数(/)
def greet(name, /, greeting="Hello"):
return f"{greeting}, {name}"
# f-string デバッグ記法
value = 42
print(f"{value=}") # value=42
3.8 はすでに EOL だが、セイウチ演算子と f-string デバッグ記法は現在も広く使われている。これらを知らずに 3.7 以前のコードを読むと混乱するので、把握しておく価値はある。
3.9(2020年10月)
# 組み込み型でジェネリクスが使えるようになった(typing 不要)
def process(items: list[int]) -> dict[str, int]:
return {str(i): i for i in items}
# 辞書の統合演算子
defaults = {"timeout": 30, "retry": 3}
overrides = {"timeout": 60}
config = defaults | overrides # {"timeout": 60, "retry": 3}
# str のメソッド強化
" hello ".removeprefix(" ") # "hello "
"hello world".removesuffix(" world") # "hello"
list[int] が使えるようになったのが地味に大きい。3.8 以前の from typing import List から解放される最初のバージョンがここだ。
3.10(2021年10月)
# Structural Pattern Matching
def classify_http_status(status):
match status:
case 200:
return "OK"
case 404:
return "Not Found"
case code if 500 <= code < 600:
return f"Server Error ({code})"
case _:
return "Other"
# エラーメッセージの改善(括弧の対応漏れを指摘してくれる)
# SyntaxError: '{' was never closed ← 3.10以降
match/case は Rust の pattern matching に触れたことがある人には馴染みやすい。ただ、3.9 以前との共存プロジェクトに使うと当然動かなくなるので注意。
3.11(2022年10月)
# トレースバックがエラー箇所を ^ で正確に示す
# 3.11以前: どの変数が None かよくわからない
# 3.11以降:
# result = obj.method().attribute
# ^^^^^^^^^^^
# AttributeError: 'NoneType' object has no attribute 'attribute'
# tomllib が標準ライブラリに追加(pip install 不要)
import tomllib
with open("pyproject.toml", "rb") as f:
config = tomllib.load(f)
print(config["project"]["name"])
# ExceptionGroup — 複数の例外をまとめて扱う
try:
raise ExceptionGroup("検証エラー", [
ValueError("email が不正"),
TypeError("age は整数が必要"),
])
except* ValueError as eg:
for e in eg.exceptions:
print(f"値エラー: {e}")
3.11 の改善されたトレースバックは、慣れると便利だ。デバッグにかかる時間が体感でかなり変わる。
3.12(2023年10月)
# f-string の制限が大幅に緩和(ネスト・バックスラッシュ・コメント可)
data = {"user": "alice", "score": 98}
msg = f"ユーザー: {data["user"]}, スコア: {data["score"] * 1.5:.1f}"
# 型パラメータ構文(TypeVar が不要に)
def first[T](lst: list[T]) -> T:
return lst[0]
class Stack[T]:
def __init__(self) -> None:
self._items: list[T] = []
# @override デコレータ
from typing import override
class BaseLogger:
def log(self, message: str) -> None:
print(message)
class MyLogger(BaseLogger):
@override
def log(self, message: str) -> None: # オーバーライドのタイポを静的に検出
super().log(f"[INFO] {message}")
3.12 から sys.monitoring が導入されるなど C API の整理が進み、古い C 拡張が壊れるケースが増えた。NumPy や pandas は対応済みだが、ニッチな科学計算ライブラリや社内パッケージでビルドエラーが出るケースがある。依存関係の確認は必須。
3.13(2024年10月)
# 実験的フリースレッドモード(GIL フリー)
# python3.13t として別ビルドが提供される
import threading
results = []
lock = threading.Lock()
def cpu_bound(n):
total = sum(i * i for i in range(n))
with lock:
results.append(total)
# GIL フリーで真の並列 CPU 実行(実験的)
threads = [threading.Thread(target=cpu_bound, args=(10**6,)) for _ in range(4)]
for t in threads: t.start()
for t in threads: t.join()
# JIT コンパイラ(実験的)
# PYTHON_JIT=1 python3.13 script.py
GIL フリーと JIT はどちらも通常版とは条件が異なるため、導入前の検証が必要だ。ただし CPython の方向性として明確に舵を切ったので、3.14・3.15 でも進化が続いている。
3.14(2025年10月7日)
t-strings の核心は「即時評価しない」ことにある。f-string が常に文字列を返すのに対し、t-string は Template オブジェクトを返す。これにより、ライブラリ側でサニタイズ処理を挟める設計が可能になった。
# t-strings(PEP 750)— f-string との違いは「即時評価しない」こと
name = "Alice; DROP TABLE users;"
# f-string: そのまま文字列に展開される(SQL インジェクション危険)
query_f = f"SELECT * FROM users WHERE name = '{name}'"
# t-string: Template オブジェクトを返す(処理を委譲できる)
query_t = t"SELECT * FROM users WHERE name = '{name}'"
# Template の構造を確認する
from string.templatelib import Template
for part in query_t:
print(type(part), repr(part))
# <class 'str'> "SELECT * FROM users WHERE name = '"
# <class 'string.templatelib.Interpolation'> Interpolation('Alice; ...', ...)
# <class 'str'> "'"
t-strings は f-string の代替ではなく、「サニタイズが必要な文脈でのフォーマット」に特化した機能だ。SQL クライアントや HTML テンプレートエンジンが内部で活用するイメージが近く、日常的な文字列フォーマットで使うものではない。
もう一つの注目点として、Zstandard 圧縮モジュール zstd が標準ライブラリに追加された。bz2 や lzma と同じ感覚で使えるようになる。
# zstd が標準ライブラリに(3.14以降、pip install 不要 / PEP 784)
import compression.zstd as zstd
data = b"compress this " * 1000
compressed = zstd.compress(data)
decompressed = zstd.decompress(compressed)
また、アノテーションの遅延評価(PEP 749)により from __future__ import annotations が不要になる方向も進んでいる。
# 前方参照が自然に書けるようになる
class Node:
def __init__(self, children: list[Node]): # 3.14以前は NameError になりうる
self.children = children
3.15(2026年10月9日)
Python 3.15 は正式リリースされた。注目していた frozendict に加えて、明示的な遅延 import、組み込みの sentinel、UTF-8 のデフォルト化が入った。
# frozendict(PEP 814)
config = frozendict({"host": "localhost", "port": 5432})
print(config["host"])
# config["host"] = "x" # 変更できない
# 明示的な遅延 import(PEP 810)
lazy import json
print("起動時点ではまだ読み込まない")
print(json.loads('{"ok": true}')) # ここで読み込む
# sentinel(PEP 661)
MISSING = sentinel("MISSING")
遅延 import は、普通の import がすべて自動で遅延されるわけではない。必要な場所で lazy を明示する方式だ。
UTF-8 がデフォルトになった点も、Windows を含むファイル入出力の挙動に関係するので注目したい。
また実験的 JIT は、公式の測定で x86-64 Linux の通常インタープリタ比で幾何平均約 7〜8% の改善と発表されている。これは「3.14 よりすべての処理が 8% 速い」という意味ではない。
公式発表:https://www.python.org/downloads/release/python-3150/
ライブラリ互換性マトリクス(更新前の参考表)
| ライブラリ | 3.8 | 3.9 | 3.10 | 3.11 | 3.12 | 3.13 | 3.14 |
|---|---|---|---|---|---|---|---|
| NumPy | 最終: 1.24 | 対応 | 対応 | 対応 | 対応 | 対応 | 対応 |
| pandas | 最終: 2.0 | 対応 | 対応 | 対応 | 対応 | 対応 | 対応 |
| TensorFlow | 対応 | 対応 | 対応 | 対応 | 要確認 | 要確認 | 未確認 |
| PyTorch | 対応 | 対応 | 対応 | 対応 | 対応 | 対応 | 未確認 |
| Django | 対応 | 対応 | 対応 | 対応 | 対応 | 対応 | 対応 |
| FastAPI | 対応 | 対応 | 対応 | 対応 | 対応 | 対応 | 対応 |
| tqdm | 対応 | 対応 | 対応 | 対応 | 対応 | 対応 | 対応 |
| cryptography | 対応 | 対応 | 対応 | 対応 | 対応 | 要確認 | 未確認 |
| 古い C 拡張 | 要確認 | 要確認 | 要確認 | 要確認 | 要確認 | 要確認 | 要確認 |
この表は元記事からの参考整理で、各ライブラリの最新版・Python 3.15 対応状況を網羅的に検証した表ではない。「対応」も全リリースの動作保証ではない。「要確認」「未確認」は「絶対動かない」ではなく、バージョン依存で動く可能性があるという意味。実際には pip install してみて初めてわかるケースが多い。本番投入前は必ず仮想環境で検証を。
古い C 拡張では、新しい Python への対応に時間がかかる場合がある。互換性は Python のバージョンだけでなく、各ライブラリのリリースによっても変わる。
バージョン間の関係を整理する
なぜ 3.11 と 3.13 を推すのか
ここが本題だ。上の比較を踏まえて、自分の考えを改めて整理する。
個人的に一番好きだったのは 3.9
from tqdm import tqdm
import time
reasons_for_39 = [
"list[int] 記法が使える最初のバージョン",
"古い C 拡張との互換性を重視しやすかった",
"当時は対応する Docker イメージや CI 構成が豊富だった",
"既存プロジェクトで使い慣れていた",
"| による辞書マージが使える",
]
for reason in tqdm(reasons_for_39, desc="3.9 が好きだった理由"):
time.sleep(0.2)
print(f" → {reason}")
正直に言うと、すでに EOL を迎えている 3.9 を今から新規プロジェクトに採用することは絶対に勧めるべきではない。セキュリティリスクは実在するし、遅かれ早かれ移行計画は立てるべきだ。
それでも、自分が一番好きだった Python は 3.9 だ。
なぜなら、現実の開発現場では「バージョンを上げたせいで古い C 拡張が壊れ、業務が止まる」ことの方が、直近の致命傷になる場面が多々あるからだ。リスクを正しく認知した上で、「list[int] が使える最低限のモダンさを保ちつつ、古い依存関係との相性を取りやすかった環境」として 3.9 に愛着がある。ただし、EOL 後の運用を推奨する意味ではない。既存環境でも移行計画は必要だ。
3.11 を推す文脈は「既存の互換性を重視するなら」
3.11 が面白いのは、このチャートで「まだそこそこ右にいながら、一気に上にジャンプしている」点だ。3.11で進んだインタープリタ高速化の恩恵を受けつつ、3.12 以降に未対応の古い依存関係をある程度まだ許容できる。2027年10月までセキュリティパッチが届く点も、長期運用するプロジェクトには安心感がある。
3.13 を推す文脈は「今から長く使うなら」
3.13 は 2029年10月までセキュリティサポートが予定されている。3.11 よりサポート期間が長く、3.14・3.15 の新機能を必須としない新規開発では有力な選択肢になると思う。
実験的な free-threaded build や JIT も試せる。ただし、通常版ですべて有効になるわけではなく、依存ライブラリの互換性確認も必要だ。
個人的には、古い依存関係との折り合いなら 3.11、今からの継続運用なら 3.13 を候補にしたい。
ユースケース別おすすめ
最後に結論
「3.11 は古すぎる」「いやいや、最新の 3.15 一択だ」——色々な意見があると思う。
しかしバージョン選択は、「新しければ正解」でも「安定していれば正解」でもないのではないだろうか。それは結局のところ、依存ライブラリ・チームの制約・EOL リスク・必要な機能、この 4 軸を自分の状況に当てはめて、泥臭く判断するのが現実的だと思っている。
この記事が、あなたの現場での「バージョン選定の根拠」を整理する材料になればうれしい。
異論・「いや 3.12 一択だろ」もコメントで待ってます。LGTM もらえると励みになります。
