概要(Mixinは「小さな能力」を後から混ぜ込むための設計)
Mixin(ミックスイン)は、クラスに“特定の機能だけ”を薄く追加するための補助クラスです。単体で使う前提の「完全な親クラス」ではなく、既存クラスへ重ねて継承し、ログ、検証、シリアライズ、リトライなどの共通処理を再利用します。核心は「小さく、単機能、状態をもたない」「cooperative(協調)継承でsuper()を使う」「命名と継承順で衝突を避ける」の3点です。
基本の考え方(いつMixinを選ぶか)
単機能の共有に最適
同じ機能を複数クラスへ配りたいなら、Mixinが強力です。例として「ログ出力」「入力検証」「JSON整形」など、どのドメインでも共通する薄い操作は“小さなクラス”に切り出して、必要なクラスに継承で混ぜ込みます。これにより重複をなくし、修正時はMixinを直すだけで全体が揃います。
継承ではなく“合成”と併用する
大きな振る舞いは合成(has-a)で持たせ、薄い機能はMixinで重ねるのが扱いやすいです。「主役のクラス」は自分の責務に集中し、共通の補助はMixinへ委ねると、設計が見通し良く保てます。
最小例(ログ機能を後から足す)
LoggingMixinの基本形
class LoggingMixin:
def log(self, msg: str) -> None:
print(f"[LOG] {msg}")
class UserService(LoggingMixin):
def create(self, name: str) -> dict:
self.log(f"create user: {name}")
return {"id": "1", "name": name}
PythonLoggingMixinは“ログを出す”だけに集中し、インスタンス変数を持ちません。必要になったクラスへ継承で混ぜれば、その場で機能が増えます。
複数ミックスインの組み合わせ(MROとsuper())
協調継承(cooperative inheritance)で安全に重ねる
複数のMixinを重ねるときは、メソッドが“重なって”呼ばれるようにsuper()を使います。PythonのMRO(メソッド解決順序)に従って、左から右へ、下から上へ協調的に流れます。
class RetryMixin:
def request(self, url: str) -> dict:
for attempt in range(3):
try:
return super().request(url) # 次のクラスへ委譲
except RuntimeError:
if attempt == 2:
raise
class AuthHeaderMixin:
def request(self, url: str) -> dict:
# ヘッダーを用意してから次へ
self._headers = {"Authorization": "Bearer TOKEN"}
return super().request(url)
class BaseClient:
def request(self, url: str) -> dict:
# 実際のHTTP(ここでは疑似)
if getattr(self, "_headers", None) is None:
raise RuntimeError("no auth")
return {"url": url, "ok": True}
class Client(RetryMixin, AuthHeaderMixin, BaseClient):
pass
print(Client().request("https://api.example.com"))
PythonMixinは「前処理をして次へ渡す」「失敗時に包んで再試行する」といった薄い責務を持ち、super()で連鎖を協調させます。順序(RetryMixin, AuthHeaderMixin, BaseClient)の並べ方が結果に影響するため、意図を明確に保ちましょう。
実務パターン(検証・シリアライズ・タイムスタンプ)
検証Mixin(入力の最低条件を守る)
class ValidateUserMixin:
def normalize_user(self, data: dict) -> dict:
uid = data.get("id")
if uid is None:
raise ValueError("id必須")
name = (data.get("name") or "unknown").strip()
return {"id": str(uid), "name": name}
Pythonどのサービスでも必要な最低限の検証・整形を“薄く”共有します。
シリアライズMixin(辞書→JSONの共通化)
import json
class JsonSerializableMixin:
def to_json(self, obj: dict) -> str:
return json.dumps(obj, ensure_ascii=False, indent=2)
PythonUI・ログ・保存などで同じ整形を多用するなら、ここへ集約して再利用します。
タイムスタンプMixin(時刻の付与)
from datetime import datetime, timezone
class TimestampMixin:
def now(self) -> str:
return datetime.now(timezone.utc).isoformat()
Python作成時刻や更新時刻の付与を“薄く”どこへでも持ち込めます。
設計の重要ポイント(単機能・無状態・命名・初期化)
単機能に絞る
Mixinは“1つの機能”を明確に切り出します。ログ+検証+I/Oのように複数責務を抱えると、使い回しが難しくなります。機能を分割して、必要なものだけ重ねて選べる形が理想です。
原則“無状態”
Mixinはインスタンス変数を極力持たず、元のクラスに依存しない関数的な実装を心がけます。どうしても状態が要る場合は、衝突しないプライベート名(例: _mixin_retry_count)に限定し、初期化の順番に配慮します。
命名規約(〜Mixin)
「LoggingMixin」「RetryMixin」のように末尾をMixinにすると、使う側が“補助目的”だと一目でわかります。ドメイン固有の主役クラスと混同しません。
initの扱いとsuper()
Mixinにinitを持たせるなら、必ずsuper().init()を呼び、他のクラスの初期化が崩れないよう協調します。可能ならinitを持たず、メソッドだけの“薄さ”を保つ方が安全です。
よくある落とし穴(衝突・順序依存・過剰継承)
メソッド/属性の衝突
同名メソッドや属性が複数Mixinに存在すると、MRO順序により期待外の上書きが起きます。名前を具体的にし、役割が被らないよう分割します。共通メソッド名(requestなど)には必ずsuper()を入れて協調させます。
並べ方次第で意味が変わる
継承リストの順序が処理の流れを決めます。Auth→Retry→Baseか、Retry→Auth→Baseかで挙動が変わるため、コメントやテストで意図を固定してください。
Mixinで“全部やる”は危険
あらゆる機能をMixin化すると、クラス宣言に大量の継承が並び、理解が困難になります。頻用の薄い機能だけをMixinにし、複雑な挙動は主役クラスや合成へ寄せます。
例題(APIクライアントに安全機能を“薄く”足す)
認証・リトライ・JSON整形を段階追加
import json, time
class AuthMixin:
def _auth_headers(self) -> dict:
return {"Authorization": "Bearer TOKEN"}
class RetryMixin:
def _with_retry(self, fn, *args, **kwargs):
for attempt in range(3):
try:
return fn(*args, **kwargs)
except RuntimeError:
if attempt == 2:
raise
time.sleep(0.1)
class JsonResponseMixin:
def _to_json(self, resp: dict) -> str:
return json.dumps(resp, ensure_ascii=False, indent=2)
class BaseClient:
def get(self, url: str, headers: dict | None = None) -> dict:
if not headers or "Authorization" not in headers:
raise RuntimeError("unauthorized")
return {"url": url, "ok": True}
class Client(AuthMixin, RetryMixin, JsonResponseMixin, BaseClient):
def get_user(self, uid: str) -> str:
url = f"https://api.example.com/users/{uid}"
headers = self._auth_headers()
resp = self._with_retry(super().get, url, headers=headers)
return self._to_json(resp)
print(Client().get_user("123"))
Python主役のClientは「何をするか」だけを記述し、認証・再試行・整形はMixinへ分離。テストでは各Mixinを個別に確認でき、差し替えや拡張が局所化されます。
まとめ(Mixinは“薄い再利用”で設計を軽くする)
Mixinは、薄い単機能を後から重ねるための補助クラスです。単機能・無状態・命名の明確化で“軽さ”を保ち、複数重ねるときはsuper()で協調継承にします。順序と衝突へ注意し、重い責務は主役クラスや合成へ。これを型として徹底すれば、初心者でも「重複のない、差し替えに強い、読みやすい」OOP設計を自然に作れるようになります。
