null / None を構造で排除する
「未設定」「該当なし」を None で表現すると、None チェックがコードのいたるところに増殖し、1箇所でも漏れれば AttributeError で落ちる。「持っていない」も立派な状態なのだから、None ではなく専用の値として設計する。常にインスタンスが存在すれば、チェックも例外も構造ごと消える。
01 確定申告の控除合計が None チェックだらけになる
class TaxReturn:
medical: Deduction | None
donation: Deduction | None
insurance: Deduction | None
def total_deduction(self) -> int:
total = 0
if self.medical is not None:
total += self.medical.amount
if self.donation is not None:
total += self.donation.amount
if self.insurance is not None:
total += self.insurance.amount
return total
def reset(self) -> None:
# 「適用なし」状態を None で塗りつぶしている
self.medical = None
self.donation = None
self.insurance = Nonefrom dataclasses import dataclass, field
@dataclass(frozen=True)
class Deduction:
name: str
amount: int
NO_DEDUCTION = Deduction("適用なし", 0)
@dataclass
class TaxReturn:
medical: Deduction = NO_DEDUCTION
donation: Deduction = NO_DEDUCTION
insurance: Deduction = NO_DEDUCTION
def total_deduction(self) -> int:
return (self.medical.amount
+ self.donation.amount
+ self.insurance.amount)
def reset(self) -> None:
self.medical = NO_DEDUCTION
self.donation = NO_DEDUCTION
self.insurance = NO_DEDUCTION02 None を返さない・渡さない
def find_donation(receipts: list[Receipt]) -> Deduction | None:
total = sum(r.amount for r in receipts)
if total == 0:
return None # 「寄附なし」を None で返してしまう
return Deduction("寄附金控除", calc_donation(total))
# 呼び出し側は毎回 None を疑わないといけない
deduction = find_donation(receipts)
label = deduction.name if deduction is not None else "適用なし"def find_donation(receipts: list[Receipt]) -> Deduction:
total = sum(r.amount for r in receipts)
if total == 0:
return NO_DEDUCTION # 「寄附なし」も立派な値
return Deduction("寄附金控除", calc_donation(total))
deduction = find_donation(receipts)
label = deduction.name # None チェック不要Python に Kotlin のような null 非許容型はないが、型ヒントから `| None` を減らし、mypy / pyright の strict モードで検査すれば「None が紛れ込んだら型エラー」という null 安全に近い体制を作れる。Optional を返す関数を見たら、まず「該当なしを表す値で置き換えられないか」を考える。