Python の type hints は実行時チェックされない
Python の type hints はこれといって書いたことがなく記法とそういうのがあるということを知ってるくらいでした

type hints 付きで書かれてるコードをいじってたときのこと
実装を変えて型が変わったのに関数の引数と返り値の型はそのままだったのですがエラーはなく動きました

def foo(num: int) -> int:
return num * 2

foo("A")
# "AA"

みたいな感じでエラーはなく動いてます
PHP みたいに実行時にチェックが入って一致しないとエラーになるのかと思いましたが 調べてみるとツールでチェックするためのもので実行時には Python 側でチェックなどはしないようですね

一応実行時にチェックさせようとするツールもあるようですが 単純に Python ファイルをそれに渡すだけじゃダメのようです
デコレーターを使ったり継承したりソースコード自体をそのツールに合わせて書く必要があるようです
型チェックのツールを変えるのにソースコードを書き換えないといけないとなると Flow から TypeScript に移行するようなもので 使いやすそうにも思えません
なので実行前に静的解析するだけで TypeScript に近い扱いみたいです

そうなると 以前話題になってた TypeScript の型記述をコメントとして JavaScript に持ってくるみたいなのも意外と現実性があったりするんでしょうか
そういえば最近全然話題になりませんが どうなったんでしょうね
ただ これとは違って Python の場合は型情報を値として持っていて実行時に参照可能です
TypeScript 構文をコメントとみなすあれは本当にコメントなので 実行時に何も残らず JavaScript からすると紛らわしいだけの無意味なものです
最悪入るとしても Python みたいに実行時に参照できるようにはなって欲しいものです

もう少し調べてみたら Python の type hints は annotations という仕組みであって type hints だけのものじゃないようです
だからか型を書くところに書けるものは型に限らず任意の式となってました

なのでこういうのも有効です

>>> import random
>>> from datetime import datetime
>>>
>>> def foo(a: random.random(), b: datetime.now(), c: [1,2,3][1:]) -> 1 + 2:
... return 0
...
>>> foo(1, 2, 3)
0
>>> foo.__annotations__
{'a': 0.8245880245015794, 'b': datetime.datetime(2023, 12, 29, 10, 16, 38, 266420), 'c': [2, 3], 'return': 3}

関数を実行した結果だったり datetime みたいなオブジェクトだったり 足し算した結果だったり
もう型のようには見えません

annotations の中で type hints を書いてるのなら dict で type みたいなキーに対して書くほうが適してそうには思います

>>> def foo(a: { "type": int }) -> { "type": str }:
... return "A" * a

これなら他の情報も追加できます
でもほとんどが type hints のみで コードが長くなるので type hints を直接書く形なのでしょうか
他のものも記載したくなったらどうするのでしょうね
dict 以外なら dict 化して値を type が key の value として追加するとかでしょうか

こういうことができるものなので Python では型は実体のある値として実行時に扱えるものです
これはすごくいいですね
C++ や C# みたいな言語だとクラスは実体がないものであって 実行時に触れられるものでもないので扱いづらく好きになれなかったです
その点 JavaScript や Python はクラスそのものもオブジェクトとして実行時に変数に入れたりプロパティを見たりなどできるものです
こういう扱いになってるのが好きなので型情報も変数に格納されてる値であるのはいいところです

ただ 変数の型については評価されるので関数は実行されますが どこにも保存されず実行時に扱えないようでした

>>> foo: 10 = 1

>>> f = lambda: print("called")
>>> bar: f() = 1
called

annotations で指定している 10 は変数 foo の情報であって foo に入ってる値の 1 に対するものではありません
foo でアクセスできるのは変数に入ってる値であって変数そのものではないので変数の情報である変数の型が取れないのは仕方なくはありますね

またジェネリクスを表現する型パラメーターというのもあります

>>> def foo[T](a: T) -> T:
... return a
...
>>> foo.__type_params__
(T,)
>>> type(foo.__type_params__[0])
<class 'typing.TypeVar'>
>>> foo.__annotations__
{'a': T, 'return': T}

これは引数みたいな感じで関数やクラスの中では変数に入ってる値として扱えます

>>> def foo[X]():      
... print(X, type(X))
...
>>> foo()
X <class 'typing.TypeVar'>
>>> class C[T]:
... print(T)
...
T
動的型付け言語でまで静的型付け言語みたいなことしないでほしい
あるプロパティ mode には "includes" か "equals" を文字列で指定するものとします
こういうのがあると一々 enum だったり定数だったりを作る人がいますよね

enum 等の型がすでにあって mode プロパティを受け取る側がその型を指定するようになっているならそれでいいです
しかし そうでもなく最終的に文字列を入れるのであれば それをわざわざ定数化したりとかムダなことはしたいとは思いません

文字列で 2 種類入れるからそういう事を考えるわけであって プロパティが mode_includes という名前で true か false を指定するのであればどうするのでしょう?
普通に true/false を使いますよね
"includes" の文字列だった部分はプロパティ名として扱うことになりましたがプロパティ名を定数や enum として扱うのでしょうか?
さすがにそんなことはしないと思います

プロパティ名であればそのまま文字列として扱うのであれば mode に入れる "includes" なども文字列としてソースコード上で扱えばいいのです