概要(superは「親の処理を安全に呼び継ぐためのバトン」)
super()は、継承チェーンの中で「次に呼ぶべきクラス」のメソッドへバトンを渡す仕組みです。単なる“親クラス呼び出し”ではなく、PythonのMRO(メソッド解決順序)に従って適切な相手に渡します。重要なのは、初期化でのsuper().init、多重継承での“協調的super”、そして「親の契約を守りながら差分だけ足す」使い方です。
基本の使い方(親の初期化とメソッド拡張)
親の初期化を呼んでから子の拡張を足す
class Animal:
def __init__(self, name: str):
self.name = name
class Dog(Animal):
def __init__(self, name: str, breed: str):
super().__init__(name) # 親の初期化(必須属性の準備)
self.breed = breed # 子の追加
def speak(self) -> str:
base = "..." # 親を拡張するなら親の実装を呼ぶこともある
return "ワン!"
Python親が定める前提(必須属性や検証)を保つため、子でもsuper().initを確実に呼びます。メソッドでも同様に、親の処理を呼んだ上で差分を足すと安全です。
親のメソッドを呼び継ぎ、振る舞いを拡張する
class Animal:
def speak(self) -> str:
return "..."
class Dog(Animal):
def speak(self) -> str:
base = super().speak() # 親の結果を生かす
return base + " ワン!"
Python「既存の仕様を保ちながら強化する」場面で、super().method()が最もシンプルに効きます。
superの本質(MROと“次を呼ぶ”という設計)
MRO(メソッド解決順序)に沿った“次のクラス”へのバトン
class A:
def setup(self): print("A")
class B(A):
def setup(self):
print("B")
super().setup()
class C(A):
def setup(self):
print("C")
super().setup()
class D(B, C):
def setup(self):
print("D")
super().setup()
D().setup()
# 出力: D → B → C → A
# D.mro() == [D, B, C, A, object]
Pythonsuper()は「親」固定ではなく、MRO上の“次のクラス”を呼びます。これにより多重継承でも重複なく一巡できます。
協調的super(各クラスがsuperを呼ぶ前提で設計)
多重継承では、関係するすべてのクラスが「自分の処理後に必ずsuperを呼ぶ」約束で設計すると、順序通りに全メソッドが呼ばれます。1つでもsuperを呼ばないクラスがあると、そこで鎖が途切れ、他の処理が実行されません。
実務での型(初期化・ミックスイン・サブクラス差分)
初期化は軽く、親の準備→子の追加の順番で
class BaseClient:
def __init__(self, api_key: str, timeout: float = 5.0):
import requests
self.timeout = timeout
self.session = requests.Session()
self.session.headers.update({"Authorization": f"Bearer {api_key}"})
class EUClient(BaseClient):
def __init__(self, api_key: str, timeout: float = 7.0):
super().__init__(api_key, timeout) # 親のセッション準備を使う
self.region = "EU"
Python親が提供する共通準備(セッションやヘッダー)を受け継ぎ、子では差分だけ持ちます。
ミックスインで“小さな能力”を重ねる
import time, requests
class RetryMixin:
def request_with_retry(self, call, tries=3, base=0.5):
for i in range(tries):
try:
return call()
except requests.RequestException:
time.sleep(base * (2 ** i))
raise
class Client(BaseClient, RetryMixin):
def get_json(self, url: str) -> dict:
def call():
r = self.session.get(url, timeout=self.timeout)
r.raise_for_status()
return r.json()
return self.request_with_retry(call)
Pythonミックスインは状態を持ち過ぎず、superで連携できる薄い機能として設計すると安全です。
重要ポイントの深掘り(正しい呼び方・引数・Pythonの仕様差)
引数なしsuper()を使う(Python 3の標準)
Python 3では、メソッド内で引数なしのsuper()が推奨です。selfやクラス名を渡す必要はありません。可読性と安全性の面でも、現代的な書き方に寄せましょう。
子のシグネチャは親の契約を守る
親が受け取る引数・返す型の契約を子でも維持します。勝手に意味を変えると置き換え可能性が壊れ、下流でバグが増えます。追加が必要ならデフォルト引数や辞書へのキー追加など、互換を保つ形に。
superを呼ぶ順序と場所
初期化では「super().init → 子の属性設定」の順が基本です。メソッドでは「前処理→super呼び出し→後処理」のどれが適切かを機能の意味で決めます(ログを先に、整形を後に、など)。
よくある落とし穴と対策(呼び忘れ・直接親呼び・多重継承の断線)
super().initの呼び忘れ
親が準備する必須属性が欠落し、実行時にAttributeErrorを招きます。テンプレート化し、コードレビューで“必ず呼ぶ”ことを確認します。
直接親を呼ぶ(Parent.method(self))は避ける
MROを無視して特定の親だけを呼ぶと、他の親に処理が回りません。多重継承で簡単に破綻します。常にsuper()を使い、チェーンを途切れさせない設計にします。
superの未協調(どこかが呼ばない)
多重継承のどこかでsuperを呼ばないと、そこから先が実行されません。ミックスイン含め「全クラスがsuperを呼ぶ」前提で作るのが鉄則です。
例題(ログ→検証→実行の順でsuperを活かすテンプレート)
前処理・親の処理・後処理を安全に鎖でつなぐ
class BaseHandler:
def handle(self, payload: dict) -> dict:
# 親の基本処理(検証や共通整形)
if "id" not in payload:
raise ValueError("id必須")
return {"id": payload["id"], "ok": True}
class LoggingHandler(BaseHandler):
def handle(self, payload: dict) -> dict:
print("[start]", payload) # 前処理(ログ)
base = super().handle(payload) # 親(検証・整形)
base["logged"] = True # 後処理(付加情報)
print("[done]", base)
return base
Pythonこの型だと、前後の付加処理を足しつつ、親の契約(検証)を確実に通過できます。さらにミックスインでリトライやキャッシュも重ねられます。
まとめ(superは「親を固定で呼ぶ」のではなく、MROで“次へ渡す”)
super()の本質は、MROに沿って“次のクラス”へ処理を渡す安全な仕組みです。初期化ではsuper().initで親の準備を確実に呼び、メソッドでは親の処理を尊重しつつ差分を足す。多重継承では協調的superで鎖を途切れさせない。直接親呼びは避け、契約(引数・戻り値)を守る。この型を体に入れれば、初心者でも継承を「壊れず、拡張しやすい」形で設計できるようになります。
