クラスを積極的に使おう!
クラスを積極的に使う理由:
リストのインデックスを利用すると可読性が下がります。インデックスに意味を持たせることになるからです。かといって辞書にも問題点があります。
辞書だと何故ダメか:
- 特定のキーが存在しているかのチェックが必要
- 辞書を引数にした関数は、他の形式の辞書で使えないため再利用性が低い(再利用性は低いが、どんな辞書でも引数の形式上受け入れてしまう)
クラスにするメリット:
- 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