Pythonのコード典型例:クラスを積極的に使おう!

クラスを積極的に使おう!

クラスを積極的に使う理由:

リストのインデックスを利用すると可読性が下がります。インデックスに意味を持たせることになるからです。かといって辞書にも問題点があります。

辞書だと何故ダメか:

  • 特定のキーが存在しているかのチェックが必要
  • 辞書を引数にした関数は、他の形式の辞書で使えないため再利用性が低い(再利用性は低いが、どんな辞書でも引数の形式上受け入れてしまう)

クラスにするメリット:

  • isinstanceでクラスのインスタンスであるか検証可能
  • 型アノテーションをすると、指定したクラスが引数に渡されるか検証可能
  • クラス名で検索すれば、そのクラスが使用されている処理を検出可能

‘dataclass’を使う理由:
可読性を高め、’__init__’メソッドの冗長さを削減することです。

‘property’を使う理由:
属性を減らすことです。

‘classmethod’を使う理由:
外部からimportするクラス・関数が一つだけで済むことです。分けた場合には’Sales’クラスとCSVから読込む関数の二つをimportする必要があります。

import csv
from dataclasses import dataclass
from typing import List

@dataclass
class Sale:
    id: int
    item_id: int
    unit_price: int
    amount: int

    def validate(self):
        if self.unit_price <= 0:
            raise ValueError("Invalid sale.price")
        if self.amount <= 0:
            raise ValueError("Invalid sale.amount")
    
    @property
    def total_price(self):
        """ インスタンス変数の変わり代わりに'property'を使う
        """
        return self.amount * self.unit_price

@dataclass
class Sales:
    data: List[Sale]

    @property
    def price(self):
        return sum(sale.total_price for sale in self.data)
    
    @classmethod
    def from_asset(cls, path="./sales.csv"):
        data = []

        with open(path, encoding="utf-8") as f:
            reader = csv.DictReader(f)

            for row in reader:
                try:
                    sale = Sale(**row)
                    sale.validate()
                except Exception:
                    continue

                data.append(sale)
            
        return cls(data=data)

例外をカスタマイズする!

# カスタマイズしたい例外の親クラスを作成
class MailReceivingError(Exception):
    pretext=''
    
    def __init__(self, message, *args):
        if self.pretext:
            message = f"{self.pretext}: {message}"
        
        super().__init__(message, *args)

# 継承した専用の例外クラスを作成
class MailConnectionError(MailReceivingError):
    pretext = '接続エラー'

class MailAuthError(MailReceivingError):
    pretext = '認証エラー'

class MailHeaderError(MailReceivingError):
    pretext = 'メールヘッダーエラー'


# 実際に使用すると…。
def newmail():
    try:
        get_newest_mail()
    except MailReceivingError as e:
        """ カスタマイズすれば継承した例外クラスは一括で補足可能
        """
        pass

コメントを残す