Diary over Finite Fields

515ひかるの日記と雑文

最近の Python の型ヒントとの付き合い方

最近 Python の型ヒントを書いたり書かなかったりしている。mypy などで型チェックして CI で落とす、みたいなことをするのがまじめにやるのがいいのだと思うのだけど、いろいろとあってまじめにやる気がしていない。

結局ライブラリ側が対応していないのでやる気がしないまま終わる。あとフロントエンドみたくどうしても JavaScript に変換しないといけないとかならともかく、Python 選んでおいてまじめに型チェックしたい気持ちがわからない。静的型付け言語使えばいいやん。

ただ不真面目に使っている分には悪くないな、と思う部分も出てきたので今日はそんな話を書く。あくまで個人の意見です。

前提として、アプリケーションレイヤーの話です。ライブラリを作るときにこの使い方は通用しないと思う。それから業務では Python ばかり書いてますが静的型付け言語を忌み嫌っているとかではない。

複雑な型は書かない

まずこれ。型変数を使わない(ジェネリクスとかしない)。型の入れ子もしない(Dict[str, List[int]] みたいな1)。表現しづらい型を書くくらいなら、クラスにするとか、諦めて Any にするとか別の方策をとる。

あと込み入った型を書こうとすると import するものが増えて、ロジックには関係ないのに型のためだけに import 文がめっちゃ増えたりする。なんでも正確に書く必要はないので、以下くらいが使いこなせていればいいと思っている。

  • Optional
  • List, list
  • Dict, dict
  • Set, set

あと TypedDict くらいは使うこともある。3.8 以上じゃないと使いにくいけど。

さっきも書いたけど、複雑な型をまじめに使いたいならそもそも Python を書かないほうがいいと個人的には思っている。

公開 API には型を書く

前提として、全部に型アノテーションをつけるのを諦めている。それをしないといけないならまず Python を書くのをやめるべき。

で、クラスのメソッドなどで当然公開しているものと公開していないものがある。公開するものだけは型を書こうという意識で最近は書いている。引数、返り値ともに。

  • 公開APIはテキストエディタで補完してくれると嬉しい
    • その API を使うときは実装を見ずに使うことが多いため
  • 欲を言うと型チェックしてくれたりすると嬉しい
  • 非公開 API とか、他で使ってないメソッドに型が書いてあるか否かはあまり気にしない
    • コードを読み書きする効率にあまり寄与しないと思う

ただし、型が書きづらいなら書かないあるいは Any でよい。書きづらいなら書かなくていい、という前提で動くと気持ちが楽。

型チェックはエディタで

CI とかにはせず、上にも書いた Pyright に任せている。自分は気が向いたら直すし、これは直すの面倒だと感じたら直さない。

github.com

型チェックでエラーが出ている状態でもマージする(というかレビュー時にしっかりと見てはいない)。テストが通っていることが前提ではあるけれど、型チェックはパフォーマンスにも挙動にも寄与しないので無視している。

想定反論

  • なんで複雑な型を使わないの?

あくまでアプリケーションレイヤー前提だけど、要は複雑な型ではなくクラス(値オブジェクト)を作ればいいと思う。型を書くんじゃなくてクラスを設計すれば済むのに、型で頑張る理由がない。Dict[str, int] なんて情報量のない型よりも List[StudentGrade] とかのほうが explicit でしょ。

  • 型チェックをしないのに型ヒントのクオリティ保つの無理じゃない?

そうだと思う。でも却ってミスリーディングな型がついているな、と思ったらその型を消せばいいだけだと思っているので、そもそもクオリティを保とうという意識が低いのだと思う。意識低くてもそんな変なことにはならない程度の型しか書いてない、というのはあるかも。

そもそも頑張って複雑な型を書かないといけない時点でなにかDB設計とかモジュール設計とかが変なんだと思う。

  • 型が嫌いなんですか

そんなことはないつもり。ただ今のところ Python で型チェックを頑張るのは徒労が大きいと思っていて、静的解析をしたいなら別の言語で開発したほうがストレスが少ないと思っている。まして、Python のユースケースは Python を使わないことができる場合も多いと思っているので、GoなりScalaなりJavaなり、他の静的型付け言語を使うほうがいいんじゃないかと思っている。

おわりに

ただのお気持ち表明。


  1. 3.9 以降は dict[str, int] みたいに書けるんだけど、さすがにまだ広く一般には使われていないと思うので大文字のもので書いた。3.9 では deprecate です。