オブジェクト指向
Python
クラス・継承・dunder・dataclass
クラスの定義
__init__・インスタンス・クラス変数・プロパティ
class BankAccount:
# クラス変数(全インスタンスで共有)
interest_rate: float = 0.02
_count: int = 0
def __init__(self, owner: str, balance: float = 0.0) -> None:
# インスタンス変数
self.owner = owner
self._balance = balance # _: 慣例的に非公開
BankAccount._count += 1
# プロパティ(getterとsetter)
@property
def balance(self) -> float:
return self._balance
@balance.setter
def balance(self, value: float) -> None:
if value < 0:
raise ValueError('残高は0以上')
self._balance = value
# インスタンスメソッド
def deposit(self, amount: float) -> None:
self.balance += amount
# クラスメソッド(cls を第一引数)
@classmethod
def get_count(cls) -> int:
return cls._count
# 静的メソッド(self も cls も不要)
@staticmethod
def validate_amount(amount: float) -> bool:
return amount > 0
# 文字列表現
def __repr__(self) -> str:
return f'BankAccount({self.owner!r}, {self._balance})'
def __str__(self) -> str:
return f'{self.owner}の口座: {self._balance}円'
acc = BankAccount('Alice', 1000)
acc.deposit(500)
print(acc.balance) # 1500
print(BankAccount.get_count()) # 1継承と多態性
super()・多重継承・抽象クラス・MRO
from abc import ABC, abstractmethod
# 抽象基底クラス
class Animal(ABC):
def __init__(self, name: str) -> None:
self.name = name
@abstractmethod
def speak(self) -> str: ... # サブクラスで実装必須
def describe(self) -> str:
return f'{self.name}は{self.speak()}と言う'
class Dog(Animal):
def speak(self) -> str: return 'ワン'
class Cat(Animal):
def speak(self) -> str: return 'ニャン'
# 多重継承
class Flyable:
def fly(self) -> str: return '飛んでいます'
class FlyingDog(Dog, Flyable):
def speak(self) -> str:
return super().speak() + '(飛びながら)'
# MRO(メソッド解決順序)
print(FlyingDog.__mro__)
# (<class 'FlyingDog'>, <class 'Dog'>, <class 'Animal'>, <class 'Flyable'>, ...)
# super() の正しい使い方
class SavingsAccount(BankAccount):
def __init__(self, owner: str, balance: float, rate: float) -> None:
super().__init__(owner, balance) # 親の __init__ を呼ぶ
self.rate = rate
def apply_interest(self) -> None:
self.deposit(self._balance * self.rate)
# isinstance と issubclass
dog = Dog('ポチ')
isinstance(dog, Dog) # True
isinstance(dog, Animal) # True(継承関係も検査)
issubclass(Dog, Animal) # Truedunder メソッド
__repr__・__eq__・__len__・__iter__・__enter__
class Vector:
def __init__(self, x: float, y: float):
self.x, self.y = x, y
# 文字列表現
def __repr__(self) -> str: return f'Vector({self.x}, {self.y})'
def __str__(self) -> str: return f'({self.x}, {self.y})'
# 演算子オーバーロード
def __add__(self, other: 'Vector') -> 'Vector':
return Vector(self.x + other.x, self.y + other.y)
def __mul__(self, scalar: float) -> 'Vector':
return Vector(self.x * scalar, self.y * scalar)
def __abs__(self) -> float:
return (self.x**2 + self.y**2) ** 0.5
# 比較
def __eq__(self, other: object) -> bool:
if not isinstance(other, Vector): return NotImplemented
return self.x == other.x and self.y == other.y
def __lt__(self, other: 'Vector') -> bool:
return abs(self) < abs(other)
# コンテナ風
def __len__(self) -> int: return 2
def __iter__(self): yield self.x; yield self.y
def __getitem__(self, i): return (self.x, self.y)[i]
def __contains__(self, v): return v in (self.x, self.y)
# コンテキストマネージャー
class DBConnection:
def __enter__(self):
self.conn = connect_db()
return self.conn
def __exit__(self, exc_type, exc_val, exc_tb):
self.conn.close()
return False # 例外を再送出
with DBConnection() as conn:
conn.execute('SELECT 1')dataclass
ボイラープレートを省略したクラス定義
from dataclasses import dataclass, field
from typing import ClassVar
@dataclass
class User:
# フィールド定義(型ヒント必須)
id: int
name: str
email: str
tags: list[str] = field(default_factory=list) # ミュータブルなデフォルト値
role: str = 'user' # イミュータブルなデフォルト値
# クラス変数(dataclass のフィールドから除外)
count: ClassVar[int] = 0
# 自動生成される:
# __init__、__repr__、__eq__
# frozen=True: イミュータブル(__hash__ も生成される)
@dataclass(frozen=True)
class Point:
x: float
y: float
def distance(self) -> float:
return (self.x**2 + self.y**2) ** 0.5
# order=True: 比較演算子を生成
@dataclass(order=True)
class Priority:
sort_index: int = field(init=False, repr=False)
level: int
name: str
def __post_init__(self):
# __init__ 後に実行
self.sort_index = self.level
# 使用
u = User(id=1, name='Alice', email='a@b.com', tags=['admin'])
p = Point(3.0, 4.0)
print(p.distance()) # 5.0