nikkie-ftnextの日記

イベントレポートや読書メモを発信

デスクリプタ素振りの記:ユーザが(object.__setattr__を持ち出しても)属性に代入できないMoneyクラス

はじめに

デスクリプタ完全に理解した! nikkieです(※エンジニアの"完全に理解した"はすぐ分からなくなるやつです)

先日のみんなのPython勉強会#98のLTをきっかけに興味を持ったデスクリプタについて素振りしました。
※触り始めで深い理解はこれからですし、間違っているかもしれません。お気づきの点がありましたら@ftnextまでお願いします

目次

stapyのLT 「5分で理解するディスクリプタ

こちらのLTのおかげで完全に理解しました🙌

金額関連の実装例があったので

デスクリプタ HowTo ガイドを参照しつつ手を動かしました

デスクリプタを使ったMoneyクラス実装例

ミノ駆動本 3章のMoneyクラスをデスクリプタで実装していきます。
過去にdataclassで実装したもの1をデスクリプタを使って書き換えます。

  • dataclassのクラス変数としてMoneyValueDescriptorインスタンス化しています`
  • MoneyValueDescriptorは一度だけ値(amount=金額)を代入できるようにしました
    • 一度だけの機会は、Moneyクラスの__init__となります
    • 金額が0以上というバリデーションはデスクリプタ側に移りました

ドキュメント参考箇所

デスクリプタにした効果

object.__setattr__2への耐性を獲得したようです!(理由はわかっていません)

dataclassで実装したときは、frozen=Trueを指定しても、object.__setattr__で値を書き換えることができてしまいました。
デスクリプタを使った実装では、この穴が塞がれています。

インスタンスの値を書き換えられないようにするのは、Pythonでは無理なんじゃないか」と考えていたので、今回発見できてよかったです。

ほか

  • 「金額が0以上」というビジネスルールがデスクリプタに切り出された
    • 他の箇所でも再利用できるかも

宿題事項

  • @dataclassの旨味が少ない
    • __init__, __repr__, __eq__全部実装した
    • デコレートしなくていいのかも
  • デスクリプタの__set_name__
    • マングリングを考慮した属性の組み立てのあたりがスッキリする?
  • money.spam = 42のようにユーザが属性を追加できる
    • dataclassではfrozen=Trueが指定してあると、例外を送出します

終わりに

初めてデスクリプタに触った素振り記事でした。
DjangoモデルやPydanticで見たあれらはデスクリプタだったのか!」と気付き、デスクリプタ HowTo ガイドからどんな実装をしていそうか、少しだけですが見当が付くようになりました。

使いこなしにはまだまだ知識が必要だと思うので、引き続き素振りしていきます