親クラスの処理も残したいけれど、自分用の処理も足したい…そんなときどうしていますか?
Python でオブジェクト指向を学び始めると、必ず出会うのが super()
関数です。
super()
は、継承関係にある親クラスのメソッドを自然な順序で呼び出すための仕組みで、メソッドのオーバーライドや多重継承、さらにはメソッド解決順序(MRO)を正しく活かすために欠かせません。今回は、IT 初心者の方でもわかりやすいように、実用的なコードと一緒に super()
の基本から多重継承でのベストプラクティスまで丁寧に解説します。
まずは一番よく使うパターンから解説します。
子クラスでメソッドをオーバーライドしつつ、親クラスの処理も呼び出したい――そんな場面で super()
が活躍します。
以下のサンプルコードを見てみましょう。
class Parent:
def __init__(self):
self.value = "親クラス"
def show_value(self):
print(self.value)
class Child(Parent):
def __init__(self):
# 親クラスの初期化を実行してから、子クラスの上書きを行う
super().__init__()
self.value = "子クラス"
def show_value(self):
# 親クラスの表示ロジックを再利用し、その後で子クラスの表示を足す
super().show_value()
print(self.value)
# インスタンスの作成と実行
child_instance = Child()
child_instance.show_value()
このコードでは、Child が Parent を継承しています。
Child.__init__()
の最初で super().__init__()
を呼ぶことで、Parent の初期化が確実に行われます。
その後で self.value を「子クラス」に変更しています。show_value() でも同様に、まず親クラスの表示ロジックを呼び出し、その上で子クラス独自の出力を追加しています。
実行すると、次の順に出力されます。
親クラス
子クラス
「親の処理を丸ごと捨てるのではなく、再利用しながら自分の処理を足す」。これが super()
の基本的な使い方です。
「親メソッドを直接呼べばいいのでは?」と思うかもしれません。
たとえば Parent.__init__(self)
のように書くこともできます。
しかし、これを多重継承でやってしまうと、同じクラスの初期化が重複したり、呼び出し順序が崩れたりして、バグの温床になります。super()
は Python が持つメソッド解決順序(MRO)に従って、関係するクラスを一回ずつ、正しい順で呼ぶ仕組みです。だから安全で拡張にも強いのです。
多重継承になると「どの親クラスから先に呼ぶの?」という疑問が出てきますよね。
ここでも super()
を使うのがベストプラクティスです。ポイントは「すべてのクラスが super()
を使って次のクラスへ委譲する」こと。これを「協調的な初期化(cooperative initialization)」と呼びます。
次のサンプルコード(多重継承のベストプラクティス)をみてみましょう。
class A:
def __init__(self, *args, **kwargs):
# 次のクラスへ委譲してから自分の初期化を行う
super().__init__(*args, **kwargs)
self.value_a = "クラス A"
print("A の初期化完了")
class B:
def __init__(self, *args, **kwargs):
# 次のクラスへ委譲してから自分の初期化を行う
super().__init__(*args, **kwargs)
self.value_b = "クラス B"
print("B の初期化完了")
class C(A, B):
def __init__(self):
# MRO(メソッド解決順序)に従って A → B → object の順で呼ばれる
super().__init__()
print("C の初期化完了")
def show_values(self):
print(self.value_a)
print(self.value_b)
c = C()
c.show_values()
ここでは C(A, B)
と定義しているため、MRO は [C, A, B, object]
という順序になります。C.__init__()
の中で super().__init__()
を一度呼ぶだけで、A → B → object の順に各 __init__()
が一回ずつ、適切に実行されます。結果として、次のような出力になります。
A の初期化完了
B の初期化完了
C の初期化完了
クラス A
クラス B
もしこれを A.__init__(self)
や B.__init__(self)
のように直接呼び分けてしまうと、将来クラスの並び順を変えたときに初期化が重複したり、どれか一方が呼ばれなくなるリスクが高まります。多重継承では、全員が super()
を使い、次のクラスにバトンを渡すことが重要です。
super() を使うときのコツと注意点は以下の通りです。
【すべての関係クラスで super()
を呼ぶ】
多重継承では特に重要です。どこか1つでも直接呼び出しにすると、MRO による「一回ずつの安全な実行」が崩れます。
【Python 3 では引数なしの super() が基本】
super()
と書くだけで、現在のクラスとインスタンスに対して適切に動作します。super(Child, self)
のような書き方は Python 2 互換用の古いスタイルです。
【初期化の順序を意識する】
多重継承で属性の上書きがある場合は、super().__init__()
を呼ぶ前後どちらで代入するかで結果が変わることがあります。迷ったら「super() を先に呼んでから自分の属性を設定」すると、後からの上書きがわかりやすくなります。
【直接呼び出しは最後の手段】
フレームワークや特殊なケースを除き、Parent.__init__(self)
のような直接呼び出しは避けましょう。MRO を無視してしまうため、拡張や保守の観点で不利です。
super()
は、Python の継承を実務レベルで安全かつ読みやすくする強力なツールです。単一継承では「親の処理に自分の処理を継ぎ足す」ために、そして多重継承では「協調的に初期化を回す」ために使います。MRO に従う super()
を正しく使えば、コードの可読性や保守性がぐっと上がります。
「親の処理を再利用しながら、子クラスで柔軟に振る舞いを変える」。そんなオブジェクト指向の醍醐味を、super()
から始めてみましょう!