1ヶ月前ほどにポストされた、PEP 661 (Sentinel Values) が気になっているので紹介しようと思います。
残念ながら現時点では Draft 状態で採用は決まっていません。
概要
- Python で引数のデフォルト値を表現するテクニックのひとつに、Sentinel values があります。
_sentinel = object()
def hello(name=_sentinel):
if name is _sentinel:
print("Hello world")
elif name is None:
print("Hello!")
else:
print(f"Hello {name}")
- このテクニックは、引数として None が渡された場合に特別な意味を持たせたい場合に役に立ちます。
- しかし、この Sentinel values には以下の欠点があります。
- repr が読みづらい
- typehints と相性が悪い (
Optional[str]
ではなくUnion[None, str, object]
というアノテーションが必要) - Sentinel value をコピー、シリアライズ/デシリアライズすると別インスタンスが生成されてしまい、比較に失敗してしまう
- この状態を改善するために、stdlib に sentinel object を導入します
アプローチ
sentinel(name)
は Sentinel object を生成します。
>>> NotGiven = sentinel('NotGiven')
>>> NotGiven
<NotGiven>
生成された Sentinel object は以下の特徴があります。
- is で比較できる
- シリアライズ/デシリアライズ耐性がある
- 短い repr 表現
感想
- 型を使うようになって、Sentinel values が邪魔になったことがあり、そこを考慮してくれるのであれば便利そう
- しかし、肝心の型アノテーションについてどうするのかが、まだ定義されていないのが残念
- 実装はシンプルだけどかゆいところに手が届く感じが気に入りました
- 採用されないかな。できれば PyPI に backport パッケージも作って欲しい。