Python 3.15を3.14と比べて検証|Shift_JIS環境で変わるopen()の既定の文字コードと、起動を速くする遅延import
Python 3.15で既定の文字コードがUTF-8に変わり、Shift_JIS系の環境に影響する
Pythonの開発チームは2026年10月9日にPython 3.15.0を公開し、10月25日にはセキュリティ修正を含む3.15.2を出しました。主な変更は、起動を速くする遅延import、変更できない辞書 frozendict などです。
日本語版WindowsなどShift_JIS系の環境で最初に確かめたいのは、ファイルを読み書きするときの既定の文字コードがUTF-8に変わった点です。open() に encoding を書いていないコードは、これらの環境では読み書きする文字コードが変わります。
この記事では、3.14.10と3.15.2を同じMacで並べて、文字コードの変化、遅延importの起動時間、新しい書き方、新しいプロファイラを確かめた結果を紹介します。読み終えると、3.15へ上げる前に自分のコードのどこを直すかを決められます。
3分で把握
encodingを書いていないopen()を探してから、3.15へ上げる
Python 3.14.10と3.15.2を、macOS 27上で比べました。
Shift_JISの環境のopen()
SJIS→utf-8
3.14で書いた日本語入りのファイルは、3.15の既定のままでは UnicodeDecodeError で読めませんでした。
遅延importの起動時間
221→11.5ミリ秒
3.15の通常のimportとlazy importの比較です。9個のモジュールをimportして使わないスクリプトでの中央値で、3.15へ上げるだけでは速くなりません。
3.14でも同じコードを動かす
__lazy_modules__
この変数なら3.14でも構文エラーにならず、3.15でだけ遅延します。
Shift_JISの環境ではopen()の既定がUTF-8に変わった
これまでのPythonは、OSや環境の設定(ロケール)に合わせて既定の文字コードを決めていました。3.15は環境にかかわらずUTF-8を既定にします。「UTF-8モード」と呼ばれる動作を標準で有効にした形です。Windowsの文書によるとOS側のコードページは残りますが、日本語版Windowsでこれまで open() の既定だったcp932も、この変更で使われなくなります。
手元ではWindowsの代わりに、Macの ja_JP.SJIS ロケールで比べました。この環境で実行すると、3.14と3.15で次の違いが出ました。
| 確かめた項目 | Python 3.14.10 | Python 3.15.2 |
|---|---|---|
| open() の既定 | SJIS | utf-8 |
| Path.write_text() で「あ」を書いたバイト列 | 82 a0 | e3 81 82 |
| io.TextIOWrapper の既定 | SJIS | utf-8 |
| sys.stdout の文字コード | shift_jis | utf-8 |
| UTF-8モード(sys.flags.utf8_mode) | 0 | 1 |
| locale.getencoding() | SJIS | SJIS(変更なし) |
macOS 27、ロケール ja_JP.SJISで2026年11月4日に確認。Windowsのコンソールへの出力は3.15より前からUTF-8なので、sys.stdoutの行はWindowsのコンソールには当てはまりません。
影響が出るのは、encoding を書かずにファイルや子プロセスの出力を読み書きしているコードと、Shift_JISを前提に標準出力を受け取るプログラムです。3.14がこの環境で書いた日本語入りのファイルを、3.15で同じコードのまま読むとエラーになりました。
$ LC_ALL=ja_JP.SJIS python3.14 -c "open('memo.txt','w').write('日本語のメモ')"
$ LC_ALL=ja_JP.SJIS python3.15 -c "print(open('memo.txt').read())"
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x93 in position 0: invalid start byte
読み込みの時点で例外になれば、原因は見つけやすいです。注意したいのは逆向きで、例外が出ない場合があります。3.15が書いた「東京都」を3.14で読むと、エラーにならず「譚ア莠ャ驛ス」と文字化けしたまま読めました。3.14が書いたログに3.15が追記した場合もエラーは出ず、2つの文字コードが混ざって、どちらの版でも読めないファイルになりました。
ファイルのほかに、子プロセスの出力も変わります。手元の環境でcp932を出力するコマンドを subprocess.run(text=True) で受けると、3.14は読めて、3.15は UnicodeDecodeError でした。encoding='cp932' を指定すると3.15でも読めます。パイプやファイルへの print() の出力もUTF-8に変わります。受け取る側がShift_JISを前提にしているなら、そちらの確認も必要です。
ロケールがUTF-8の環境では、open() の既定は3.14でも3.15でもUTF-8で、読み書きの結果は同じでした。ロケールがUTF-8のMacやLinuxでは、この変更で困る場面はほとんどありません。
読み書きするファイルに合わせて3つの直し方を選ぶ
文字コードが決まっているファイル
encoding='cp932' や encoding='utf-8' と書きます。どの版、どの環境でも同じように動くので、まず選びたい方法です。
OSの設定に合わせたいファイル
encoding='locale' と書きます。手元の ja_JP.SJIS の環境で、3.14が書いたファイルを正しく読めました。
すぐにコードを直せない場合
PYTHONUTF8=0 か -X utf8=0 で以前の動作に戻します。手元でも正しく読めました。直すまでのつなぎとして使います。
直す場所は、Pythonの警告で探せます。-X warn_default_encoding を付けて実行すると、実行された open() のうち encoding のない行に EncodingWarning が出ました。3.14でも使えるので、更新の前に今のコードで確かめられます。print() の出力先の変化はこの警告では見つからないので、Shift_JISのまま受け取るプログラムは別に確かめます。
$ python3.14 -X warn_default_encoding app.py
app.py:1: EncodingWarning: 'encoding' argument not specified
使わないモジュールなら遅延importで起動時間が大きく縮む
3.15の新しい構文 lazy import は、モジュールの読み込みを最初に使う瞬間まで遅らせます。これまでは関数の中へ import を移して同じことをしていましたが、ファイルの先頭にまとめて書いたまま遅らせられます。
効果を見るため、asyncio や ssl など9個の標準モジュールをimportし、使わずに終わるスクリプトの実行時間を31回ずつ測りました。
9個のモジュールをimportして使わないスクリプトの実行時間(中央値)
3.14:通常のimport:212.9ミリ秒
3.15:通常のimport:221.3ミリ秒
3.15:lazy import:11.5ミリ秒
3.15:__lazy_modules__:11.5ミリ秒
3.15:通常のimportに -X lazy_imports=all:11.5ミリ秒
何も書かない空のスクリプトは、どちらの版も11.5ミリ秒でした。macOS 27、Apple Silicon、31回実行の中央値。使わないモジュールだけの例なので、使えば読み込みの時間はその時点でかかります。
-X lazy_imports=all は、コードを書き換えずに通常の import をまとめて遅延させる起動オプションです。読み込みの失敗の時期も変わるので、効果を測る用途に向きます。コードに書く場合の形は次のとおりです。3.14では lazy の行が構文エラーになるので、両方の版で動かすライブラリには __lazy_modules__ が向いています。3.14はこの変数を普通の変数として無視し、通常どおり読み込みました。
lazy import json # 3.15のみ。3.14では SyntaxError
__lazy_modules__ = ['json'] # 3.14でも動く。遅延するのは3.15だけ
import json
読み込みの失敗が表に出る時期も変わります。存在しないモジュールを lazy import しても、その行は通り、最初に使う行で ModuleNotFoundError になりました。起動時に必ず確かめたい依存関係は、通常の import のまま残します。手元では、関数の中、try の中、from … import * に lazy を付けると SyntaxError になりました。__lazy_modules__ は対象の import より前に書きます。
frozendictなど3つの新しい書き方は短いコードで試せる
構文と組み込み型の追加は、次の3つを両方の版で試しました。いずれも3.15で動き、3.14では NameError か SyntaxError になります。結果は行ごとにコメントで示しました。
# 変更できない辞書。辞書のキーにも使える
d = frozendict(a=1, b=2)
{d: 'key'}[frozendict(b=2, a=1)] # 'key'(順序は比較に影響しない)
d['a'] = 9 # TypeError
# 「引数なし」を None と区別するための値
MISSING = sentinel('MISSING')
def f(x=MISSING):
return 'なし' if x is MISSING else x
f(), f(None) # ('なし', None)
# 内包表記の中で展開する
[*x for x in [[1, 2], [3]]] # [1, 2, 3]
{**d for d in [{'a': 1}, {'b': 2}]} # {'a': 1, 'b': 2}
設定値のように書き換えられたくない辞書には frozendict、「指定なし」の目印を自作していた箇所には sentinel が使えます。どちらも3.14では動かないので、3.14をサポートするライブラリでは最低バージョンを上げるまで待つことになります。
新しいプロファイラTachyonはMacでは管理者権限が要る
3.15には、動いているプロセスの処理を一定間隔で記録するプロファイラ profiling.sampling(Tachyon)が入りました。速度のボトルネックを探す道具で、コードを変更せず、動作中のプロセスにも使えると説明されています。
手元のMacで python3.15 -m profiling.sampling run を実行すると、プロセスのメモリを読む権限がないというエラーで止まり、sudo での再実行を案内されました。公式のDockerイメージ python:3.15-slim では、追加の設定なしで動き、関数ごとの時間を表示しました。コンテナの中で動かすコードを測るなら、Dockerから始めると管理者権限を使わずに済みます。
$ docker run --rm -v "$PWD":/w python:3.15-slim \
python -m profiling.sampling run --pstats /w/prof.py
Captured 793 samples in 0.79 seconds
Sample rate: 999.37 samples/sec
Error rate: 23.58
nsamples sample% tottime (ms) ... filename:lineno(function)
416/416 68.6 416.000 ... prof.py:3(slow.<locals>.<genexpr>)
190/606 31.4 190.000 ... prof.py:3(slow)
表の prof.py は、重い計算を繰り返す短い試験用のスクリプトです。Error rate は読み取りに失敗した標本の割合で、手元の実行では2〜3割でした。そのぶん、表の標本の合計は取得数より少なくなっています。
3.15へ上げる前に確かめる4つのこと
- 今の版で
-X warn_default_encodingを付けてテストを実行し、encodingのない読み書きを洗い出す。 - 今の版が書いた日本語入りのファイルを3.15で読むテストを足し、Windowsを使う人がいるならWindowsでも実行する。
- 公式の変更点の「Removed」を読み、使っている機能が消えていないかを確かめる。たとえば
http.serverのCGIHTTPRequestHandlerは、手元でもImportErrorになりました。 - 起動の遅いコマンドラインツールがあれば、
-X lazy_imports=allで起動時間を測り、効果を見てから遅らせるモジュールをlazyで指定する。
私なら、3.15へ上げるかどうかにかかわらず、まず1番目の警告だけを今のコードで試します。encoding を明示する修正は3.14でもそのまま役に立ち、更新のときの失敗も減らせます。
参考資料
- What's new in Python 3.15(Python公式ドキュメント、2026年11月4日に閲覧)。UTF-8の既定、遅延import、frozendict、sentinel、内包表記、Tachyon、削除された機能
- Using Python on Windows(Python公式ドキュメント、2026年11月4日に閲覧)。WindowsのUTF-8モードとコードページ
- Python 3.15.0(python.org、2026年10月9日)。正式版の公開
- Python 3.15.2(python.org、2026年10月25日)。検証に使った版
- PEP 686、PEP 810(Python Enhancement Proposals)。UTF-8の既定と遅延importの提案












