他の人のPython嫌いを助長させないプログラマーになろう

# tldr & 動機  
- Python は初心者向けの言語として名高い
    - 従って Python ができる人ではなくて、プログラム歴自体短く **Python しかできない人**が少なくない
        - 誰しも何にしても初めてはあるのでそれ自体を咎めるつもりはない。（それを咎める人は新しい分野を最近学んでいない臆病者。）あくまで**これ人に出せるモンじゃねえぞ**という話
- 従って**設計面以前にコード整頓のセンスが絶望的なものが散見される**
    - 家の整理整頓術的に言うと、**トイレに炊飯器置いてあるレベルのものがある。** 逃げたい。
- n番煎じであるのは理解している。そのため**書き手の実体験**の側面を強調したい。
    - つまり「よくありがちなこういうク◎がこのように改善できる」ということ。**愚痴と紙一重でもある。**
    - 体系だったものが欲しいなら[別の記事](https://kobzol.github.io/rust/python/2023/05/20/writing-python-like-its-rust.html)・[書籍](https://www.oreilly.co.jp/books/9784814400171/)を当たればよい。
# disclaimer 
上記で触れていますが、
- 胸糞要素あり
- 散文であり体系的ではない
- n番煎じ
です。それが嫌な方は退出願います（別に大したこと書いてないです）
# それでは実例
## データ型定義しろ辞書型乱用するな【基本】
### BAD!
```python
def process_data(item:dict[str,str]) -> dict[str,str]:
    """
    item dict[str, str] この関数が処理するインプットだよ♡
    """
    connection_string = item["foofoo"]
    # 1234 行後
    nanka_shiran = item["property"]
    ret:dict[str,str] 
    ret["bazbaz"] = nanka_shiran_hensu
    # 2345 行後
    return ret
```
process_data というネーミングセンスは一旦さておき（後述するので）、**型付けが完全に自己目的化してしまっていて、何の助けになっていません**。 
**これでは何のキーにどのような情報が設定されている `dict[str,str]` を渡す必要があるのかソースコードから読み解かないといけない**です。
「タイプヒント書いてる俺偉い」みたいにうぬぼれていそうでム◎つくまであります。
```python
def main():
    # process_data 関数を使いたいな！
    input:dict[str,str] = {
        "kokoni": "naniwo settei sureba iino"
    }
    process_data(input) #型ヒントもコメントも何の参考にもならずに困る
```
**コードが増えていくに従って認知しておかなければいけない範疇が無限に広がっていくので、このように中の実装を厳密に追いかけないと碌に使えない関数は万◎に値します。**

## GOOD!
```Python
import dataclasses

@dataclasses.dataclass
class Input:
    """
    foofoo:str 接続文字列 みたいな感じでここにコメント書いても良い
    """
    foofoo:str 
    property:str
@dataclasses.dataclass
class Output:
    bazbaz:str

def process_data(item:Input) -> Output:
    """
    関数のコメントを引き続き書きたいなら書いても良い
    """
    connection_string = item.foofoo
    # 1234 行後
    nanka_shiran = item.property
    nanka_shiran_hensu = "shiran"
    ret = Output(nanka_shiran_hensu)
    # 2345 行後
    return ret
```
上記のようにして適切に`class`を定義してやることにより、**この関数はこれを入れれば動いてくれて、これが返ってくるんだ**というのが分かりやすくなった。

また、 **IDEに易しい**。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3926441/74120469-a314-49ef-bbf1-3a5461c9e52b.png)
私はPython専門ではないので（諸々の事情で自分で選ぶ言語ではないので）詳細なお作法が間違っていたらすみませんが、**ちゃんと書けばこうやって利用者に明示的にドキュメントを読ませずに使わせてあげられるんですよ**。

また仮にコメントが不十分でコードを追いかける必要が出たとしても、**IDEにはフィールドの参照箇所を総検索するという機能が一般的についているので、少なくとも辞書型の時と比べると遥かに楽**になります。
![image2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3926441/4561c74b-5361-4e90-a14d-20a8de5f6e01.png)
※スクショ内で使用している VS Code が IDE に属するのかという点は本質的ではないので議論の対象としません。
### dataclasses.dataclass って何
詳細は割愛しますが：
- データの集合（構造体）の要素が強い時（例：座標。`{x:123, y:222, z:111}` みたいな）は、これを使ってやればよいです。`def __eq__` などが自動でくっついてくるため同じデータか比較することがあるとき、そのロジックとかを書かなくてよいです。（詳細は [operator overloading](https://medium.com/data-science/overloading-operators-in-python-2e24da0d36d7)などで検索、他の言語にも同じような機能あります。）
- データが状態を内包していたり勝手に編集されたりしたくないなどのオブジェクトの要素が強いときは `@dataclasses.dataclass` なしで使えばよいです。

この辺りは慣れというか、自動車運転でいう所の「**車線変更も出来ないのに、適切なルートプラニングの心配をしても話が分かるわけないだろう**」という感じですね。
またルートプラニングと同じで絶対的な正解があるわけではなく、経験論的なところも多分にあります。

:::note info
興味があれば例によって [stackoverflow](https://stackoverflow.com/questions/4514582/whats-the-difference-between-an-object-and-a-struct-in-oop) にそのような記事があるので読んでみると良いと思います。
:::

## データ型定義しろ辞書型乱用するな【応用】
### BAD!!!
```python
def nanka_shorisuru(item:dict[str,str]):
    #1234行後
    ret :dict[str,str]
    return {
        "status_code": 123,
        "hokano_meta_data":"yaaa~~~y!!!!",
        "hoshikatta_atai":ret
    }
def nanka_shorisuru2(item:dict[str,str]):
    #1234行後
    ret :dict[str,str]
    return {
        "status_code": 122,
        "hokano_meta_data":"haaa~~~y!!!!",
        "hoshikatta_atai":ret
    }

def main():
    nanka_shorisuru({"hogehoge":123123})
```
正直理由がわからないのですが、関数からの戻り値に対していちいちステータスコード的なものを含めているというケースが存在しました。はっきり言っていろんな次元で意味不明ではあります。
- 同じエラーだとしても、呼び出し元のコンテキストによってハンドリングが変わることは大いにある。従ってエラーの内容を伝えるのは分かるがステータスコード（＝呼び出し元の終わり方）まで指定するのはなんかおかしい。
    - 人間の例：ダメ元依頼と、本命の依頼、二つでは拒絶されたときの対応が異なる。後者ならリトライしたいが、前者ならそんなに力まない、とかとか。
- Rustでいうところの `Result` 型みたいに既に参考にする実装があるわけで、[実際に試行した実装もある](https://github.com/rustedpy/result)のになぜこうなったのか

と色々残念ではありますが、やるにしても**型ヒントの恩恵を受けられるような努力**はするべきです。

:::note info
実際に利用したい値のほかにメタデータを含んでいる実装は珍しくなく、例えばC#だと[boxing](https://en.wikipedia.org/wiki/Boxing_(computer_programming))された版、やや関連して `Nullable<T>` 版のものとかもそうだと思うので、実データのフィールドの名前はその辺を参考にするのが良いと思います。
:::
### GOOD!
```python
import dataclasses
@dataclasses.dataclass
class ReturnType[T]:
    status_code:int
    hokano_meta_data:str
    value: T    
@dataclasses.dataclass
class Input:
    foofoo:str 
    property:str
@dataclasses.dataclass
class Output:
    bazbaz:str
    
def nanka_shorisuru(item:Input) ->ReturnType[Output]:
    ret = Output("shiran")
    return ReturnType(123,"yaaa~~~y!!!!",ret)
    
# もう一つの関数も同じような感じ
```
**Pythonの型ヒントでもジェネリクスを使うことができ、このように他のデータを包むデータ構造を作りたい場合に便利**です。これをするとIDEに易しく利用者の認知負荷がぐっと下がります。
**関数の型ヒントから `.value` の値は `Output`型に決まっているのだからということで、そのフィールドである`bazbaz`が一番上に表示されていますね**。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3926441/8326debe-c39c-4c91-930a-d3d122811f00.png)

:::note info
Tの型を制限したい（例えば `1 == 1` みたいに == で評価できることを確約したい）場合は`Protocol`という機構があるようですが、もうその辺まで来ると脳のワーキングメモリ的な問題からコンパイラが整合性をある程度面倒みてくれる静的型付け言語に乗り換えるのが良いのではという感じはします。
ただし実経験には基づかないです。何故ならこの記事を書く程度には現行のPythonのプロジェクトはコードが酷いからです。
:::
## 適切な関数名をつける
### BAD
```python
def send_email(item:Email):
    #なんかファイルの上書きとかしている
    pass
```
見ての通りなのですが、メールを送るという関数名なのにその名前から直感的に察することができないことをしています。前にも言った通りコードが増えていった時に名前から処理が察せないと、**思わぬところで[副作用](https://en.wikipedia.org/wiki/Side_effect_(computer_science))があってバグになった**みたいなことが発生しますし、あと単純にわかりにくいのでコードの再利用しにくいです。
また、**先の例で出した `process_data` みたいなものは最悪です。「食べ物を食べた」と同じで、何のヒントにもなら**ず、 与えるものはストレスだけです。幾多と出来るであろう関数の中から選ぶのですから、名前を読むだけでこれが欲しかったものだと分かることが望ましいです。

## 関心の分離(separation of concerns) ができていない
### BAD!
```python
from typing import Generator
class A:
    pass 

def read_file() -> Generator[A, None,None]:
    # ここで A のフィールドの成型を行う
    pass

def save_to_db() -> None:
    # ここでも A のフィールドの成型を行う
    pass
```
先ほどの名前と実情が一致していないというのにかなり関連します。
上記の例だと、**読みだされたインプットを起点にして保存されるデータに至るまでで行われる加工処理の総数を洗い出すのに複数の関数を読まないといけません**。しかも、この例だと特に変形処理を行っていることが関数名からは察することができないのでたちが悪いです。
# takeaways
- 型ヒントを適切に使用し、self-documenting なコードを心がけましょう
    - self-documenting というのは「実装読めば分かるからドキュメント要らないよね」の意味ではありません。そうすると認知範囲が無限に増えるのでいつか詰みます。そうではなく、関数名・シグネチャ・データ型などでそれらを表現しようという事です。
- コードの再利用性、および関数の名実を一致させましょう
# 備考
初手として良いか疑問は残りますが、貼っておきます。
[.NETのクラスライブラリ設計　改訂新版　開発チーム直伝の設計原則、コーディング標準、パターン](https://honto.jp/ebook/pd_31268356.html?cid=nscl_rd)