- Python
Python 継承とsuper()の使い方|__init__と多重継承MRO図解
Python の継承とは、既存のクラス(親クラス)の属性とメソッドを引き継いだ新しいクラス(子クラス)を作る仕組みで、super() は「MRO(メソッド解決順序)で自分の次にあるクラス」のメソッドを呼ぶための組み込み関数です。 class 子(親): と書いて継承し、子クラスの __init__ では super().__init__(...) で親の初期化を呼びます。
この記事では、super().__init__() を書き忘れたときの AttributeError と、引数が合わないときの TypeError を全文つきで再現し、直し方とセットで示します。ひし形継承で各 __init__ が呼ばれる順番は、print の出力・__mro__・図の3つで対応づけました。**kwargs での引数の受け渡し、Mixin の並べ順、dataclass の __post_init__、@override(Python 3.12 以降)まで、Python 3.14.7 で実行した結果を載せています。
継承と super() の早見表
| やりたいこと | 書き方 | ポイント |
|---|---|---|
| 継承する | class Child(Parent): |
親の属性・メソッドがそのまま使える |
親の __init__ を呼ぶ |
super().__init__(name) |
self は渡さない。子で __init__ を定義したら必ず書く |
| メソッドをオーバーライドする | 子クラスで同じ名前のメソッドを定義 | 親の処理も使うなら super().メソッド名() |
| クラスメソッドで親を呼ぶ | super().create() |
@classmethod の中でも同じ書き方 |
| プロパティで親を呼ぶ | super().title |
かっこは付けない |
| 探す順番を確認する | Child.__mro__ / Child.mro() |
左から順に探す。最後は object |
| 多重継承する | class Child(Left, Right): |
左に書いたクラスが先に探される |
| 多重継承で引数を渡す | def __init__(self, *, color, **kwargs): と super().__init__(**kwargs) |
自分の引数だけ受け取り、残りを次へ渡す |
| Mixin を足す | class Item2(LoudMixin, Item): |
Mixin を左、本体を右に並べる |
| dataclass で親の後処理を呼ぶ | super().__post_init__() |
親が __post_init__ を定義している場合に書く |
| オーバーライドを明示する | @override(from typing import override) |
Python 3.12 以降。型チェッカーが綴り違いを見つける |
| 継承関係を調べる | isinstance(obj, Parent) / issubclass(Child, Parent) |
子クラスのインスタンスは親クラスのインスタンスでもある |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Python 継承とsuper()の使い方|__init__と多重継承MRO図解」
この記事の検証環境
| 項目 | 版 |
|---|---|
| Python | 3.14.7(@override の比較用に 3.11.15 も使用) |
| mypy | 2.4.0 |
| OS | Linux |
出力はすべて上記で実行したものです。トレースバック内のファイルのパスは /home/taro/ に置き換えています(表示は環境に合わせて置き換えています)。Python 3.11 以降ではトレースバックに ^ や ~ の目印が付きますが、最後の行のエラーメッセージは 3.11.15 でも同じ文面でした。
継承の基本形:super().__init__() とオーバーライド
会員クラスを継承して、ランクを持つ会員クラスを作ります。
class Member:
def __init__(self, name):
self.name = name
def greet(self):
return f"{self.name}です"
class PremiumMember(Member):
def __init__(self, name, rank):
super().__init__(name) # 親の初期化を先に呼ぶ
self.rank = rank # 子クラス独自の属性を足す
def greet(self): # オーバーライド
base = super().greet() # 親の処理を再利用
return f"{base}(ランク: {self.rank})"
m = PremiumMember("佐藤", "gold")
print(m.greet())
print(m.name, m.rank)
print(isinstance(m, Member), issubclass(PremiumMember, Member))
print(PremiumMember.__mro__)
佐藤です(ランク: gold)
佐藤 gold
True True
(<class '__main__.PremiumMember'>, <class '__main__.Member'>, <class 'object'>)
押さえる点は3つです。
- 子クラスで
__init__を定義すると、親の__init__は自動では呼ばれません。super().__init__(...)を自分で書きます。 - 子クラスで
__init__を定義しなければ、親の__init__がそのまま使われます。 super().greet()のように、オーバーライドしたメソッドの中から親の同名メソッドを呼べます。処理を置き換えるのではなく「足す」ときに使います。
エラーになる例と直し方(全文つき)
super().__init__() を書き忘れたときの AttributeError
class Member:
def __init__(self, name):
self.name = name
def greet(self):
return f"{self.name}です"
class PremiumMember(Member):
def __init__(self, name, rank):
# super().__init__(name) を書き忘れている
self.rank = rank
m = PremiumMember("佐藤", "gold")
print(m.rank)
print(m.greet())
gold
Traceback (most recent call last):
File "/home/taro/e1.py", line 17, in <module>
print(m.greet())
~~~~~~~^^
File "/home/taro/e1.py", line 6, in greet
return f"{self.name}です"
^^^^^^^^^
AttributeError: 'PremiumMember' object has no attribute 'name'
インスタンスの作成と m.rank の表示までは進み、親のメソッドが self.name を読んだところで止まります。エラーの行(6行目)は親クラスの中ですが、原因は子クラスの __init__ にあります。「子クラスのオブジェクトに、親が設定するはずの属性がない」というメッセージを見たら、まず super().__init__() の呼び忘れを確認します。
直し方:子クラスの __init__ の先頭に super().__init__(name) を足します(前の節の完成形)。
引数が合わないときの TypeError 3パターン
(1) 親に渡す引数が足りない
class PremiumMember(Member):
def __init__(self, name, rank):
super().__init__() # name を渡し忘れ
self.rank = rank
PremiumMember("佐藤", "gold")
Traceback (most recent call last):
File "/home/taro/e2.py", line 12, in <module>
PremiumMember("佐藤", "gold")
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
File "/home/taro/e2.py", line 8, in __init__
super().__init__() # name を渡し忘れ
~~~~~~~~~~~~~~~~^^
TypeError: Member.__init__() missing 1 required positional argument: 'name'
(2) self を余分に渡している
class PremiumMember(Member):
def __init__(self, name, rank):
super().__init__(self, name) # self を余分に渡している
self.rank = rank
PremiumMember("佐藤", "gold")
Traceback (most recent call last):
File "/home/taro/e3.py", line 12, in <module>
PremiumMember("佐藤", "gold")
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
File "/home/taro/e3.py", line 8, in __init__
super().__init__(self, name) # self を余分に渡している
~~~~~~~~~~~~~~~~^^^^^^^^^^^^
TypeError: Member.__init__() takes 2 positional arguments but 3 were given
super() は self を結び付けた状態でメソッドを返すので、self は書きません。「2 positional arguments」の2つは self と name で、そこへ self・self・name の3つが渡った、という意味です。
(3) 子クラスを作るときの引数が足りない
PremiumMember("佐藤") # 子クラスの __init__ は rank も必要
Traceback (most recent call last):
File "/home/taro/e4.py", line 12, in <module>
PremiumMember("佐藤") # 子クラスの __init__ は rank も必要
~~~~~~~~~~~~~^^^^^^^^
TypeError: PremiumMember.__init__() missing 1 required positional argument: 'rank'
エラーメッセージの読み方
TypeError のメッセージの先頭にあるクラス名が、引数の合っていない __init__ の持ち主です。
| メッセージ | どこを直すか |
|---|---|
'PremiumMember' object has no attribute 'name' |
子の __init__ に super().__init__(...) を足す |
Member.__init__() missing 1 required positional argument: 'name' |
子の中の super().__init__() に name を渡す |
Member.__init__() takes 2 positional arguments but 3 were given |
super().__init__(self, name) の self を外す |
PremiumMember.__init__() missing 1 required positional argument: 'rank' |
インスタンスを作る側で rank を渡す |
super() の仕組み:「親」ではなく「MRO の次」
super() は「親クラスを呼ぶ」と説明されることが多いのですが、正確には 「そのオブジェクトのクラスの MRO で、いまのクラスの次にあるクラス」 から探します。MRO(Method Resolution Order、メソッド解決順序)は、属性やメソッドを探すクラスの順番で、クラス.__mro__ で確認できます。
単一継承では「MRO の次」と「親」が同じなので、違いは表に出ません。違いが出るのは多重継承のときです。
メソッド以外でも同じ書き方で使えます。
class Base:
def hello(self):
return "Base"
@classmethod
def create(cls):
return f"Base.create -> {cls.__name__}"
@property
def title(self):
return "base"
class Child(Base):
def hello(self):
return "Child > " + super().hello()
@classmethod
def create(cls):
return "Child.create > " + super().create()
@property
def title(self):
return super().title + "+child"
c = Child()
print(c.hello())
print(Child.create())
print(c.title)
print(super(Child, c).hello()) # 引数つき super:Child の次から探す
print(Child.mro() == list(Child.__mro__))
Child > Base
Child.create > Base.create -> Child
base+child
Base
True
2行目で cls.__name__ が Child になっているとおり、super() 経由で呼んでも、self や cls は元のオブジェクト・クラスのままです。
引数なしの super() は、クラス定義の中に書かれたメソッドでだけ使えます。クラスの外で定義した関数をあとから代入した場合は、次のエラーになります。
def outside(self):
return super().hello()
Child.outside = outside
c.outside()
RuntimeError: super(): __class__ cell not found
このような場合は super(Child, self).hello() と引数つきで書きます。
ひし形継承:__init__ が呼ばれる順番を出力・MRO・図で対応づける
Left と Right がどちらも Base を継承し、Child が Left と Right の両方を継承する形を、ひし形継承(ダイヤモンド継承)と呼びます。
flowchart TD
Child --> Left
Child --> Right
Left --> Base
Right --> Base
Base --> object
すべて super() で書いた場合
class Base:
def __init__(self):
print(" Base.__init__ 開始")
print(" Base.__init__ 終了")
class Left(Base):
def __init__(self):
print(" Left.__init__ 開始")
super().__init__()
print(" Left.__init__ 終了")
class Right(Base):
def __init__(self):
print(" Right.__init__ 開始")
super().__init__()
print(" Right.__init__ 終了")
class Child(Left, Right):
def __init__(self):
print(" Child.__init__ 開始")
super().__init__()
print(" Child.__init__ 終了")
print("Child():")
Child()
print("MRO:", [c.__name__ for c in Child.__mro__])
print("Left():")
Left()
print("MRO:", [c.__name__ for c in Left.__mro__])
Child():
Child.__init__ 開始
Left.__init__ 開始
Right.__init__ 開始
Base.__init__ 開始
Base.__init__ 終了
Right.__init__ 終了
Left.__init__ 終了
Child.__init__ 終了
MRO: ['Child', 'Left', 'Right', 'Base', 'object']
Left():
Left.__init__ 開始
Base.__init__ 開始
Base.__init__ 終了
Left.__init__ 終了
MRO: ['Left', 'Base', 'object']
「開始」の並びが、MRO の並びと一致しています。
| 順番 | 実行中の __init__ |
その中の super() が指す先(MRO の次) |
|---|---|---|
| 1 | Child |
Left |
| 2 | Left |
Right(親の Base ではない) |
| 3 | Right |
Base |
| 4 | Base |
object(この例では呼んでいない) |
flowchart LR
A["1. Child"] --> B["2. Left"] --> C["3. Right"] --> D["4. Base"] --> E["object"]
注目したいのは2行目です。Left の中の super().__init__() は、Child から呼ばれたときは Right を、Left() として単独で作ったときは Base を呼びます。 同じコードでも、行き先は「いま作っているオブジェクトのクラスの MRO」で決まります。その結果、Base.__init__ は1回だけ実行されます。
親クラス名を直接呼ぶ書き方との違い
同じ構成を、Base.__init__(self) のように親クラス名を指定して書いた場合です。
class Base:
def __init__(self):
print(" Base.__init__")
self.connections = getattr(self, "connections", 0) + 1
class Left(Base):
def __init__(self):
print(" Left.__init__")
Base.__init__(self) # 親クラス名を直接指定
class Right(Base):
def __init__(self):
print(" Right.__init__")
Base.__init__(self)
class Child(Left, Right):
def __init__(self):
print(" Child.__init__")
Left.__init__(self)
Right.__init__(self)
c = Child()
print("Base.__init__ が呼ばれた回数:", c.connections)
Child.__init__
Left.__init__
Base.__init__
Right.__init__
Base.__init__
Base.__init__ が呼ばれた回数: 2
Base.__init__ が2回実行されました。初期化で接続を開いたり、一覧に登録したりする処理があると、2重に行われます。
2つの書き方を混ぜた場合も確認しました。Left だけが Base.__init__(self) と書き、ほかは super() を使っています。
class Left(Base):
def __init__(self):
print(" Left.__init__")
Base.__init__(self) # ここだけ親クラス名を直接指定
class Right(Base):
def __init__(self):
print(" Right.__init__")
super().__init__()
class Child(Left, Right):
def __init__(self):
print(" Child.__init__")
super().__init__()
Child()
Child.__init__
Left.__init__
Base.__init__
Right.__init__ が一度も呼ばれていません。 Left が MRO の次(Right)を飛ばして Base を直接呼んだためで、エラーにはならず静かに初期化が抜けます。
| 書き方 | Base.__init__ の回数 |
Right.__init__ |
向いている場面 |
|---|---|---|---|
すべて super().__init__() |
1回 | 呼ばれる | 多重継承を含む場合の基本 |
すべて 親クラス名.__init__(self) |
2回 | 呼ばれる | 呼ぶ順番を自分で完全に決めたい特殊な場合 |
| 混在 | 1回 | 呼ばれない | 避ける(初期化が抜ける) |
クラス階層の中では super() にそろえるのが、わかりやすく扱いやすい方針です。
MRO を作れない並べ方
基底クラスの並べ方によっては、矛盾のない順番を作れず、クラス定義の時点でエラーになります。
class Base:
pass
class Left(Base):
pass
class Broken(Base, Left): # 親を先、子を後に並べている
pass
Traceback (most recent call last):
File "/home/taro/mro_err.py", line 9, in <module>
class Broken(Base, Left): # 親を先、子を後に並べている
pass
TypeError: Cannot create a consistent method resolution order (MRO) for bases Base, Left
MRO には「子クラスは親クラスより先」「かっこの中に書いた順番を保つ」という2つの決まりがあります(C3 線形化と呼ばれるアルゴリズムです)。Broken(Base, Left) は「Base を Left より先に」と書いていますが、Left は Base の子なので「Left を Base より先に」も必要になり、両立しません。class Broken(Left, Base): と、子に近いクラスを左に書けば解決します。
多重継承の引数は **kwargs で受け渡す
各クラスの __init__ が違う引数を取る場合、位置引数のままでは、MRO の次のクラスに引数が合わなくなります。
class Base:
def __init__(self, name):
self.name = name
class Left(Base):
def __init__(self, name, color):
super().__init__(name)
self.color = color
class Right(Base):
def __init__(self, name, size):
super().__init__(name)
self.size = size
class Child(Left, Right):
def __init__(self, name, color, size):
super().__init__(name, color)
self.size = size
Child("椅子", "白", 3)
Traceback (most recent call last):
File "/home/taro/kwargs_ng.py", line 24, in <module>
Child("椅子", "白", 3)
~~~~~^^^^^^^^^^^^^^^^^
File "/home/taro/kwargs_ng.py", line 20, in __init__
super().__init__(name, color)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
File "/home/taro/kwargs_ng.py", line 8, in __init__
super().__init__(name)
~~~~~~~~~~~~~~~~^^^^^^
TypeError: Right.__init__() missing 1 required positional argument: 'size'
Left は Base を呼ぶつもりで super().__init__(name) と書いていますが、Child の MRO では次が Right なので、size が足りなくなります。
直した形:キーワード専用引数と **kwargs
class Base:
def __init__(self, *, name, **kwargs):
super().__init__(**kwargs) # 最後は object.__init__() に届く
self.name = name
class Left(Base):
def __init__(self, *, color, **kwargs):
super().__init__(**kwargs) # 自分の引数だけ取り、残りは次へ
self.color = color
class Right(Base):
def __init__(self, *, size, **kwargs):
super().__init__(**kwargs)
self.size = size
class Child(Left, Right):
pass # __init__ を書かなくても連鎖する
c = Child(name="椅子", color="白", size=3)
print(vars(c))
print([k.__name__ for k in Child.__mro__])
try:
Child(name="椅子", color="白", size=3, weight=5)
except TypeError as e:
print("TypeError:", e)
{'name': '椅子', 'size': 3, 'color': '白'}
['Child', 'Left', 'Right', 'Base', 'object']
TypeError: object.__init__() takes exactly one argument (the instance to initialize)
この形は「協調的な多重継承」と呼ばれます。約束事は3つです。
| 約束事 | 理由 |
|---|---|
各クラスは自分の引数だけをキーワード専用(*, の後ろ)で受け取る |
位置がずれる心配がなくなる |
残りは **kwargs で受け取り、super().__init__(**kwargs) でそのまま次へ渡す |
次がどのクラスになっても引数が届く |
すべてのクラスが super().__init__() を呼ぶ |
途中で連鎖が切れない |
vars(c) の並びが name・size・color の順になっているのは、各クラスが先に super().__init__() を呼び、戻ってきてから自分の属性を設定しているためです(Base → Right → Left の順に設定)。
3行目の TypeError は、どのクラスも受け取らない weight が最後まで残り、object.__init__() に届いたものです。余分な引数に気づけるという点で役に立ちますが、メッセージに引数名は出ないので、この文面を見たら「どのクラスも受け取っていないキーワード引数がある」と読み替えます。
Mixin の並べ順:Mixin を左、本体を右
Mixin(ミックスイン)は、単独では使わず、ほかのクラスに機能を足すための小さなクラスです。
import json
class JsonMixin:
def to_json(self):
return json.dumps(vars(self), ensure_ascii=False)
class LoudMixin:
def describe(self):
return super().describe().upper() + "!"
class Item:
def __init__(self, name, price):
self.name = name
self.price = price
def describe(self):
return f"{self.name}: {self.price} yen"
class GoodItem(LoudMixin, JsonMixin, Item): # Mixin を左、本体を右
pass
class OddItem(Item, LoudMixin): # 本体を左に置いた場合
pass
g = GoodItem("desk", 12000)
print(g.describe())
print(g.to_json())
print([c.__name__ for c in GoodItem.__mro__])
o = OddItem("desk", 12000)
print(o.describe())
print([c.__name__ for c in OddItem.__mro__])
DESK: 12000 YEN!
{"name": "desk", "price": 12000}
['GoodItem', 'LoudMixin', 'JsonMixin', 'Item', 'object']
desk: 12000 yen
['OddItem', 'Item', 'LoudMixin', 'object']
OddItem では Item.describe が先に見つかり、LoudMixin.describe は使われませんでした。エラーは出ないので、気づきにくい動きです。
| Mixin の種類 | 例 | 並べ順の影響 |
|---|---|---|
| 新しいメソッドを足すだけ | JsonMixin(to_json を追加) |
どこに置いても動く |
| 既存メソッドに処理をかぶせる | LoudMixin(describe を上書きし super() を呼ぶ) |
本体より左に置く |
LoudMixin は object しか継承していないのに super().describe() と書けています。これも「super() は MRO の次を見る」ためで、GoodItem の MRO では LoudMixin の先に Item があるので、Item.describe に届きます。
dataclass の継承と __post_init__
dataclass は __init__ を自動で作るので、super().__init__() を書く場面はありません。初期化後に追加の処理をしたいときは __post_init__ を使い、親にも __post_init__ があるなら super().__post_init__() を呼びます。
from dataclasses import dataclass, field
@dataclass
class Member:
name: str
email: str = ""
def __post_init__(self):
print(" Member.__post_init__")
self.name = self.name.strip()
@dataclass
class PremiumMember(Member):
rank: str = "silver"
label: str = field(init=False)
def __post_init__(self):
super().__post_init__() # 親の後処理も呼ぶ
print(" PremiumMember.__post_init__")
self.label = f"{self.name}[{self.rank}]"
m = PremiumMember(" 佐藤 ", "sato@example.com", "gold")
print(m)
Member.__post_init__
PremiumMember.__post_init__
PremiumMember(name='佐藤', email='sato@example.com', rank='gold', label='佐藤[gold]')
フィールドは「親のフィールド → 子のフィールド」の順に並びます。そのため、親に既定値つきのフィールドがあり、子に既定値なしのフィールドを足すと、クラス定義の時点でエラーになります。
@dataclass
class Member:
name: str
email: str = ""
@dataclass
class PremiumMember(Member):
rank: str # 既定値つきの email の後ろに、既定値なしの rank が来る
TypeError: non-default argument 'rank' follows default argument 'email'
(トレースバックの最後の行。途中は dataclasses モジュール内部の行が続きます。)
直し方は2つです。子のフィールドにも既定値を付けるか、kw_only=True でキーワード専用にします。
@dataclass(kw_only=True)
class Member:
name: str
email: str = ""
@dataclass(kw_only=True)
class PremiumMember(Member):
rank: str
print(PremiumMember(name="佐藤", rank="gold"))
PremiumMember(name='佐藤', email='', rank='gold')
@override でオーバーライドを明示する(Python 3.12 以降)
typing.override は、「このメソッドは親のメソッドをオーバーライドしている」と明示するデコレータです。PEP 698 で定義され、Python 3.12 で追加されました(今回 PEP 698 のページを開いて確認)。
from typing import override
class Member:
def greet(self) -> str:
return "こんにちは"
class PremiumMember(Member):
@override
def greet(self) -> str:
return "ようこそ"
@override
def great(self) -> str: # 綴り違い:親に great はない
return "ようこそ"
m = PremiumMember()
print(m.greet(), m.great())
print(PremiumMember.greet.__override__, PremiumMember.great.__override__)
Python 3.14.7 で実行した結果です。
ようこそ ようこそ
True True
実行時にはエラーになりません。 @override は、メソッドに __override__ = True という目印を付けるだけで、親に同名メソッドがあるかどうかは調べません。調べるのは型チェッカーです。mypy 2.4.0 の結果です。
ov.py:15: error: Method "great" is marked as an override, but no base method was found with this name [misc]
ov.py:21: error: "Callable[[PremiumMember], str]" has no attribute "__override__" [attr-defined]
Found 2 errors in 1 file (checked 1 source file)
1行目が目的の指摘で、綴り違いの great を見つけています(2行目は、確認用に __override__ を直接読んだ行への指摘です)。親クラスのメソッド名を変更したときに、子クラス側の直し漏れを見つける用途にも使えます。
Python 3.11.15 で同じファイルを実行すると、import の時点で止まります。
ImportError: cannot import name 'override' from 'typing' (/usr/lib/python3.11/typing.py)
3.11 以前でも使いたい場合は、typing_extensions パッケージに同名の override が用意されています(こちらは今回未実行です)。
補足:サブクラスが作られたときに処理をする __init_subclass__
__init_subclass__ は、そのクラスを継承したクラスが定義されるたびに呼ばれる特殊メソッドです。ここでも super().__init_subclass__(**kwargs) を呼んでおくと、多重継承で連鎖が切れません。
class Plugin:
registry = []
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
Plugin.registry.append(cls.__name__)
class CsvPlugin(Plugin): pass
class JsonPlugin(Plugin): pass
print(Plugin.registry)
['CsvPlugin', 'JsonPlugin']
継承を使うかどうかの判断表
| 状況 | 向いている方法 |
|---|---|
| 「A は B の一種」と言える(プレミアム会員は会員の一種) | 継承 |
| 親の処理の一部だけを差し替えたい | 継承+オーバーライド+super() |
| 複数のクラスに同じ小さな機能を足したい | Mixin |
| 「A は B を持っている」と言える(注文は明細を持つ) | 属性として持たせる(コンポジション) |
| データを入れる器が中心で、初期化の記述を減らしたい | dataclass |
よくある質問
Q. super().__init__() は __init__ の最初と最後のどちらに書きますか?
A. 多くの場合は最初に書きます。親が用意する属性を、子の処理で使えるようにするためです。親の __init__ が子の属性を読む設計になっている場合などは、先に子の属性を設定してから呼ぶこともあります。
Q. super(Child, self) と super() は何が違いますか?
A. クラス定義の中のメソッドでは同じ意味で、Python 3 では引数なしの super() が短く書けます。この記事の実行結果でも、super(Child, c).hello() は Base を返しました。クラスの外で定義した関数など、引数なしでは RuntimeError: super(): __class__ cell not found になる場面で引数つきを使います。
Q. 親の __init__ が引数を取らない場合も super().__init__() は必要ですか?
A. 親の __init__ が属性の設定などの処理をしているなら必要です。親が __init__ を定義していない場合は呼ばなくても動きますが、多重継承で使われる可能性のあるクラスでは、連鎖を保つために書いておくと扱いやすくなります。
Q. 多重継承で、同じ名前のメソッドはどちらが使われますか?
A. MRO で先にあるクラスのものが使われます。class Child(Left, Right): なら Left が先です。迷ったら Child.__mro__ を表示して確かめます。
まとめ
- 子クラスで
__init__を定義したらsuper().__init__(...)を書く。書き忘れると、親のメソッドが属性を読むところでAttributeError: ... has no attribute ...になる super()にselfは渡さない。TypeError のメッセージ先頭のクラス名が、引数の合っていない__init__の持ち主super()は「親」ではなく「MRO の次」を呼ぶ。ひし形継承ではChild → Left → Right → Baseの順に呼ばれ、Base.__init__は1回だけ実行された- 親クラス名を直接呼ぶ書き方では
Base.__init__が2回、混在させるとRight.__init__が呼ばれなかった。階層の中はsuper()にそろえる - 多重継承の引数は、キーワード専用引数と
**kwargsで受け渡す - 処理をかぶせる Mixin は本体より左に並べる。順番は
__mro__で確認できる - dataclass は
super().__post_init__()、オーバーライドの明示は@override(実行時には検査されず、型チェッカーが綴り違いを見つける)
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Python 継承とsuper()の使い方|__init__と多重継承MRO図解」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。