HN 日本語サマリー

← 一覧へ戻る
プログラミング

Much Ado About None: Pythonにおけるセンチネル値

Much Ado About None: Sentinel Values in Python (florianwilhelm.info)

12 pointsby sebg2 コメント

要約

Pythonの`None`は、値が存在しない、未設定、エラー発生、あるいは実際の値そのものなど、多岐にわたる意味で使われるため曖昧さが生じます。この記事では、この`None`の曖昧さを解消するためにセンチネル値(例えば`object()`や新しい組み込み関数`sentinel`)を使用することを推奨しています。センチネル値は有効な値の範囲外に存在するため、値との衝突を防ぎ、コードの可読性と安全性を向上させます。

全文翻訳

Much Ado About None: Pythonにおけるセンチネル値

NoneはおそらくPythonで最も多用される値です。私たちはこれを、何かが欠落している、未設定である、エラーが発生した、あるいは単に実際の値として表現するためのスイスアーミーナイフのように使用します。私が今年PyConDE/PyData Berlin 2026、EuroPython 2026、そして最近ではSwiss Python Summit 2026で発表したセンチネル値に関する私のトークでは、なぜこの曖昧さが問題を引き起こすのか、そして代わりに何を使うべきなのかを詳しく見ていきます。

問題は多くの場所で現れます。単純なキャッシュは、キーが存在しない場合と、キーがNoneにマッピングされている場合の両方でNoneを返します。設定更新におけるtimeout=Noneは「タイムアウトなし」を意味するのか、それとも「現在の値を維持する」を意味するのか?そして、TimeoutErrorを飲み込んでNoneを返す関数は、それをエラー信号として使用します。センチネル値は、有効な値のドメインの外側にガードとして存在することで、これを解決します。そのため、実際の値と衝突することはありません。

Python自体も、目に隠されたセンチネルに大きく依存しています。EllipsisはNumPyのスライシングで次元をスキップし、スタブを示し、NotImplementedは「実装をどこかで見つけろ」という意味で、__rsub__のようなメソッドを介した二項演算子の二重ディスパッチを可能にします。PydanticのMISSINGは、nullに明示的に設定されたフィールドと、まったく送信されなかったフィールドを区別し、Pydantic 2.14で実験段階を終えたばかりです。

独自のセンチネルを作成すると、事態は複雑になります。古典的なMISSING = object()は実行時には完璧に機能しますが、静的型チェッカーでの型ナローイングを壊します。TypeIsガード、マジックLiteral文字列、または単一メンバーEnumトリックのような回避策はすべてトレードオフを伴い、センチネルがコピーされたりピクル化されたりすると、アイデンティティチェックは壊れます。

そして、プロットツイストが起こりました。4月のPyConDEで、センチネルを標準化する提案であるPEP 661が延期されたと結論付けなければなりませんでした。わずか9日後、ステアリングカウンシルはそれを承認しました。Python 3.15以降、sentinelは組み込みとなり、型チェッカーはNoneと同様にそれをナローイングできます。

MISSING = sentinel("MISSING")

def update_email(user_id: int, email: str | None | MISSING = MISSING) -> None:
    if email is MISSING:
        return  # 更新なし
    elif email is None:
        db.delete_email(user_id)
    else:
        db.set_email(user_id, email.lower())  # strにナローイングされる

古いPythonバージョンを使用している場合は、typing_extensionsがバックポートを提供しています。型チェッカーのサポートはまだ追いついていないため、それに依存する前に確認してください。

私の主なテイクアウェイは、Noneの曖昧さに注意し、実際に何を表現したいのかを考えることです。センチネルを使用して曖昧さを解消し、ライブラリが既に提供しているものを優先し、古いobject()とEnumのハックを新しい組み込み関数に置き換えてください。例外を発生させる代わりにNoneを返す誘惑に駆られた場合は、例外と結果型に関する私のトークも興味深いかもしれません。

PyConDEからのスライドは、PEP 661がまだ延期されていると説明しています。以下のEuroPythonの録画は、トークの最も最新バージョンであり、新しいsentinel組み込み関数をカバーしています。