はじめに
背伸びのVenus♪1 nikkieです。
ライブラリopenai-pythonの素振りに励むnikkie氏。
「利用者が〇〇と書くだけで、なんでこんなことができちゃうんだろう」と気になったところは、ソースコード(裏の仕組み)も手短に確認します。
そんな中で新たに知ったsentinel valuesをエントリにします。
目次
- はじめに
- 目次
- openai-pythonの型ヒントを見ていて
- PEP 661 – Sentinel Values を垣間見
- あれらって全部sentinel valuesの実装だったのか!
- 終わりに
- P.S. ミノ駆動本から、Noneに未設定という意味を持たせるのは悪いコードでは?
openai-pythonの型ヒントを見ていて
例えばAssistantsをcreateするメソッド(client.beta.assistants.create
)の型ヒント
https://github.com/openai/openai-python/blob/v1.2.3/src/openai/resources/beta/assistants/assistants.py#L40-L56
def create( self, *, model: str, description: Optional[str] | NotGiven = NOT_GIVEN,
description引数の型NotGiven
とデフォルト値NOT_GIVEN
ってそれぞれなんだろう?と気になりました。
これらはVS Codeでのコーディング中、提示される型ヒントでもチラチラ見えていました。
import元をたどるとパッケージルートの_types.py
です。
https://github.com/openai/openai-python/blob/v1.2.3/src/openai/_types.py#L271-L293
NOT_GIVEN = NotGiven()
ともあります2。
デフォルト値にしていたNOT_GIVEN
はNotGiven
クラスのインスタンスでした(シングルトンのようです)
NotGiven
クラスのコメントに興味深い一文が
Sentinel class used until PEP 0661 is accepted
PEP 661 – Sentinel Values を垣間見
Abstractを簡単に確認します(原文に即すというよりかは今の理解を書いています)。
sentinel value(直訳して、番兵の値)とは、唯一の予め指定されている値(Unique placeholder values)
例えば
- 値が与えられていないことを示すための引数のデフォルト値
def foo(value=None):
(Noneを使う例)
- 文字列のfindメソッドが、見つからないときに返す-1
1つ目の例のようにNone
が使われることが多いですが、None
をsentinel valueとは区別したい場合は別の値が使われます3。
(PEPを離れますが)上で見たNOT_GIVEN
はNone
をsentinel valueとは区別したい場合に該当しますね。
description引数には何らかの文字列かNone
が指定される可能性があり、いずれも指定されない場合はデフォルト値のNOT_GIVEN
となります。
このPEPでは標準ライブラリ(stdlib)にsentinel valuesの実装を追加することを提案しています4。
参照実装(Reference Implementation)は https://github.com/taleinat/python-stdlib-sentinels にあるとのことですが、PEPのstatusはDraftですし、議論がどうなっているか気になるところです。
ですが、今回はPEP 661の確認はここまでとします。
あれらって全部sentinel valuesの実装だったのか!
さて、PEP 661を覗いたことでこれまでに見た実装のいくつかがsentinel valuesでつながりました!
pandasで見かけたNoDefault
https://github.com/pandas-dev/pandas/blob/v2.1.3/pandas/_libs/lib.pyx#L2820-L2832
class _NoDefault(Enum): no_default = "NO_DEFAULT" no_default = _NoDefault.no_default # Sentinel indicating the default value.
pandasのコードではlib.no_default
として使われています。
https://github.com/search?q=repo%3Apandas-dev%2Fpandas%20no_default&type=code
気づいたきっかけはassert_almost_equal
でした5。
https://github.com/pandas-dev/pandas/blob/v1.4.2/pandas/_testing/asserters.py#L65
def assert_almost_equal( check_less_precise: bool | int | NoDefault = no_default,
なお、v2.1.3ではこの引数はなくなっていました。
Djangoで見かけた:=
(ウォルラス)
https://github.com/django/django/blob/4.2.5/django/core/management/utils.py#L164
def run_formatters(written_files, black_path=(sentinel := object())):
:=
により、引数black_path
のデフォルト値はobject()
が返すインスタンス(☆)です。
(☆)のインスタンスは変数sentinel
でも指しています。
関数本体ではif black_path is sentinel:
という分岐があり、そのブロックではblack_path
が指定されていないので、その場合の処理をしています
初見で「???」となりましたが、やりたいことはsentinel valuesというのが理解のきっかけとなりました。
終わりに
openai-python中のコメントをきっかけに、Pythonでsentinel valuesを実装するいくつかのやり方を見てきました
さて、Zen of Pythonにこういう一節がありますよね。
There should be one-- and preferably only one --obvious way to do it.
Zen of Pythonに対して自己矛盾しているように思われます7。
また、PEP 661にはdrawback(欠陥)という語が登場しており8、いろいろなやり方がある(実際、標準ライブラリで実装されている)中でまずい実装もあるようです。
まとめる中で気になってきたので、PEP 661は時間をとって読んでみようと思います。
P.S. ミノ駆動本から、Noneに未設定という意味を持たせるのは悪いコードでは?
『良いコード/悪いコードで学ぶ設計入門』9.6です。
未装備をNone(書籍ではJavaなのでnull)で表す悪しきコードを改善します。
Noneを使うのをやめて、未装備状態を表す定数を用意しました。
sentinel valuesについてもこの論は当てはまるのではないかと私は考えています。
気軽にNone
をsentinel valueの意味で使うよりは、標準ライブラリにある方が好ましいように思えます(Zen of Pythonとの矛盾を解決できるという点でも)
-
この曲も今回取り上げたトピックもSVと略せますね
↩ - https://github.com/openai/openai-python/blob/v1.2.3/src/openai/_types.py#L297↩
- 「However, sometimes an alternative sentinel value is needed, usually when it needs to be distinct from None.」(PEP 661 Abstract)↩
- 「This PEP proposes adding a utility for defining sentinel values, to be used in the stdlib and made publicly available as part of the stdlib.」(PEP 661 Abstract)↩
- pandasコードリーディング会#5 - connpassで読みました↩
- Django 4.1から環境変数PATHにblackが見つかれば、startprojectやstartappで作ったファイルがフォーマットされるようになってる〜〜!!! - nikkie-ftnextの日記 の宿題にも今回取り組んだ形になります↩
- 召喚した天使様「やり方は1つだけをスローガンとするPythonなのに、sentinel valuesはいくつもの実装方法があるとか、舐めているのですか」↩
- 「However, the common implementations, including some in the stdlib, suffer from several significant drawbacks.」(PEP 661 Abstract)↩