小ネタ:値オブジェクトで読みやすく(Pythonを例に)

Event

ラクス リーダブルコード LT会 - vol.2

Presented

2021/07/07🎋 nikkie

❓ Question(チャット💬 お願いします🙏)

みなさん、ふだんどの言語を書いてますか?

インタラクティブにやってみるので、LT中の Zoomチャット大歓迎 です!

お前、誰よ

お前、誰よ(承前)

  • Love Python!

  • Love anime!!(🎺🎷🔥 🌲🌳🐲 📽🎞🎥🎬🧡 🍎🍋🥝🍇🧺)

  • PyCon JP 2021 座長🇨🇭

LTするので宣伝させてください🙏

  • Py thon Con ference as PyCon(=年に一度のPython祭り)

  • PyCon JP 2021 10/15(金), 16(土)

  • 📣 7/10(土) までトークのプロポーザル募集中!! 📣 募集開始ブログ

ここから本題:#readablelt

可読性の高いコードやリファクタリングについてLTでアウトプットしましょう!

connpass リーダブルコード LT会 - vol.2

リーダブルといえば

リーダブルコード――より良いコードを書くためのシンプルで実践的なテクニック』🎼

💬 読んだ方?🙋‍♂️ 聞いたことはある方?🙋‍♀️

私と『リーダブルコード』🎼

  • 新卒1年目のとき、会社でおすすめされて読んだ

  • 「他の人が最短時間で理解できるように」(1章)👉 読みやすさ

  • 命名 が特に印象的(他にも早期リターンなど)

「カラフル」🌈

もっと「カラフル」な単語を探す(2章)

  • 情報が伝わる

  • 目的に適した明確な名前

    気取った言い回しよりも明確で正確なほうがいい。(2章)

カラフルを求めて🌈

  • 変数や関数をカラフルな単語で命名(愚直に実践)

  • カラフル優先で、単語数が増え、変数名長くなりがち🤯

  • 当時の悩み:カラフルの加減 ムズイ当時のLTスライド

読みやすさについての最近の気づき

  • 実践してきた命名だけではなさそう

  • 『リーダブルコード』には 外側 がある!

  • 気づきの中から、値オブジェクト を共有します

値オブジェクト

💬 知ってる方?🙋‍♂️ 聞いたことはある方?🙋‍♀️

値オブジェクトと読みやすさの気づき

値を扱うための専用クラスとは?

  • 基本データ型を 属性 として1つか2つ

  • コンストラクタで値の範囲の検証(完全コンストラクタ)

  • 不変(値オブジェクトを使った計算では、新しい値オブジェクトを返す)

例:Money (金額を扱うクラス)


@dataclass(frozen=True)
class Money:
    value: int  # 基本データ型を属性として1つ

    def __init__(self, value: int) -> None:
        if value < 0:  # 完全コンストラクタ(金額は0円以上)
            raise ValueError("不正: 0未満")
        object.__setattr__(self, "value", value)

例:Money (金額を扱うクラス)続き


def multiply(self, number: int) -> Money:
    """Moneyを整数倍した、新しいMoneyを作って返す(不変)

    >>> Money(500).multiply(3)
    Money(value=1500)

    """
    return Money(self.value * number)

別の例:Quantity (数量を扱うクラス)


@dataclass(frozen=True)
class Quantity:
    value: int  # 基本データ型の属性

    def __init__(self, value: int) -> None:
        if value < 0:  # 完全コンストラクタ
            raise ValueError("不正: 0未満")
        object.__setattr__(self, "value", value)

値オブジェクトで読みやすく

  • 『現場で役立つシステム設計の原則』から読みやすいと思った例

  • 注:Pythonの型ヒントが絡みます

補足:Pythonの型ヒント

  • 変数や関数の仮引数・返り値の型を表す ラベル (コメントに近い)

  • 実行時に型をチェックするわけでは ない

詳しくは 過去の登壇資料 をどうぞ

値オブジェクトを使わない場合

引数の順番、分かりづらくないですか?(型ヒントがともに int。第1引数は金額?個数?)


def amount(unit_price: int, quantity: int) -> int:
    """総額計算"""
    return unit_price * quantity

値オブジェクトを使うと


def amount(unit_price: Money, quantity: Quantity) -> Money:
    # quantity.as_int()で Quantity型からint型に変換
    return unit_price.multiply(quantity.as_int())

読みやすい

値オブジェクトで読みやすくなった🙌

  • 型ヒントを手がかりに、理解にかかる時間が短くなっている印象

  • 値オブジェクトで、どんな値を処理しているか が明確に

  • 例えば、unit_priceMoney (金額)であることは、命名の工夫ではここまで短くできないのでは?

さらに、エディタの補完、わかりやすい!

引数に何をどんな順番で渡せばいいかが明確!(VSCodeでの例)

../_images/202107_readable_type_hint_in_vscode.png

値オブジェクト & 型ヒントで

  • 値オブジェクトによる 不変性

  • 静的型チェック(例: Money を渡していないと気づける)

  • 副次的に読みやすく

とはいえ、シルバーバレットではない

  • デメリット: 小さいクラスたくさん になるので、実装量は増える

  • デメリットを軽減する方法を見つけるべく、プライベートで書くスクリプトでやっていき💪

まとめ🌯:値オブジェクトで読みやすく

  • 『現場で役立つシステム設計の原則』で値オブジェクトを知った

  • 値オブジェクトと型ヒントで、関数が読みやすくなる!

  • 『リーダブルコード』の外側(設計で読みやすく する)に気づいた

ご清聴ありがとうございました

📣 PyCon JP 2021は 7/10(土) までトークのプロポーザル募集中!🙏

関連アウトプット、Appendix、いただいたコメントへの返答が続きます(よろしければどうぞ!)

関連アウトプット

Appendix: NewType

https://docs.python.org/ja/3/library/typing.html#newtype

静的型検査器は新しい型を元々の型のサブクラスのように扱います。


>>> from typing import NewType
>>> Money = NewType("Money", int)
>>> money = Money(1000)

Appendix: NewType

値オブジェクトと比較すると、不変でない


>>> Money(1000) + Money(500)  # return int
1500

完全コンストラクタも実装されていません

Future works

  • PyDantic 導入?

    • 本文では標準ライブラリの dataclasses モジュールを使った実装を紹介

    • PyDantic は dataclasses + 実行時の型チェック

LT中のコメント・質疑から

  • たくさんのコメントありがとうございました!

  • 勉強会の盛り上がりを感じました

みなさん、ふだんどの言語を書いてますか?

  • いろいろな言語が挙がりました(サーバサイド〜フロントエンド)

  • Python使いの方もちらほら

『リーダブルコード』読んだ方?🙋‍♂️ 聞いたことはある方?🙋‍♀️

  • 読んだ方が多め

  • 会社の「おすすめ技術本で殿堂入り」

値オブジェクト、知ってる方?🙋‍♂️ 聞いたことはある方?🙋‍♀️

  • (ここは飛ばしすぎてコメントする余裕が作れなかったかもしれません)

  • 2本目のしげ丸さんの Primitive Obsession の話にも通じますね

💬 小ネタへの感想

不変オブジェクトは考えることが減って安心して使えて嬉しいです。

ドメインに処理を持たせるの良いですよね!

💬 動的型付けの Python でも値オブジェクトの恩恵はあるんですねー

  • nikkieはソフトウェアエンジニアリングを極めたい!志向なので恩恵はあると思います

  • ただPythonを適用する分野によっては使っても恩恵は得られにくいかと思います(例:機械学習)

💬 動的型付けの Python でも値オブジェクトの恩恵はあるんですねー(続き)

  • PythonでDDDの例:https://github.com/iktakahiro/dddpy

  • 動的型付け言語ではありますが、例えば静的型チェッカー mypy をエディタでファイル保存時に走らせて、実行する前に書き間違えに気づけるようにもできます

💬 PyCharm と vs code どちらが使いやすいですか?

  • nikkieはVSCodeしか使っていない(比較していない)ので、偏った回答になります

  • 他のPython使いにも聞いていただくといろいろな意見が集まって参考にしやすいと思います

値オブジェクトのデメリットの面についてコメント(感謝)

  • 💬 「書く量が増えるかわりに読むスピードはあがる」

  • トータルコストで考える視点はありませんでした。コメントありがとうございます

🐦 関数名の語順

🐦 関数名の語順 への考え

  • getよりはfetchでしょうか(まず目が行きました)

  • 語順は後者にすると思います(getRetiredUser 「retired userを取得」と読めるので)

  • 前者だと SVC文型 で、user を retired にするとも読めちゃうかもしれません

  • 状況によっては getUser(status.Retired) のようなメソッドにするかも

EOF