本記事について
Djangoのソースコードを見ていたところ、クラスのオーバーライドにsuper()
を用いているものがたくさんありました。super()
とは何なのか、用いないでオーバーライドする場合と何が違うのかを整理します。
オーバーライドとは
基底クラスと同じ名前のメソッドを派生クラス内に定義することです。基底クラスのメソッドに機能を追加・変更をすることができます。
基底クラスを定義する
# 基底クラス
class Coffee(object):
def __init__(self, color, fragrance, price, taste, elapsed_time):
self.color = color
self.fragrance = fragrance
self.price = price
self.satisfaction = ((taste + fragrance) * price) - elapsed_time
def drink(self):
print(f'満足度が{self.satisfaction}点のホーフィーを飲む')
coffee = Coffee('brown', 10, 200, 10, 300)
coffee.drink() # <--- 満足度が3700点のホーフィーを飲む
基底クラスを定義したので、以下にsuper()
を使った場合とそうでない場合の派生クラスを作ってみます。(両者とも機能は同じ)
基底クラスをオーバーライドする(super()を使わない)
Coffee
クラスを継承し、メソッドをオーバーライドしたクラスを作ってみます。
# Coffeeを引き継いだ後、機能を拡張したい!!
class Override(Coffee):
# 普通にオーバーライドすると基底クラスの処理が無効化されてしまうので、処理を拡張させたいなら基底クラスと同じ処理を記述する必要がある。
def __init__(self, color, fragrance, price, taste, elapsed_time, size):
self.color = color
self.fragrance = fragrance
self.price = price
self.satisfaction = ((taste + fragrance) * price) - elapsed_time + size * 55
self.size = size
def size_up(self):
self.price += 100
self.size += 1
print(f'サイズが{self.size}になった!代わりに価格が高くなった...')
# 基底クラスのメソッドも、再度イチから定義しなければならない
def drink(self):
print(f'満足度が{self.satisfaction}点のホーフィーを飲む')
print(f'サイズが{self.size}はちょっと多すぎたかな...')
# 実行結果 普通のオーバーライド
override = Override('brown', 10, 200, 10, 300, 10)
override.drink() # <--- 満足度が4250点のホーフィーを飲む, サイズが10はちょっと多すぎたかな..
override.size_up() # <--- サイズが11になった!代わりに価格が高くなった....
基底クラスの__init__()
とdrink()
メソッドをオーバーライドしました。
普通にオーバーライドすると基底クラスの処理が無効化されてしまうので、単純に処理を拡張させたい場合は基底クラスと同じ処理を記述する必要があり、冗長的なコードになってしまいます。
super()を使ってオーバーライドする
class SuperOverride(Coffee):
# Super関数で基底クラスのコンストラクタを呼び出すことで、初期化のコードはもう書かなくて良い
def __init__(self, color, fragrance, price, taste, elapsed_time, size):
super().__init__(color, fragrance, price, taste, elapsed_time,)
self.satisfaction = ((taste + fragrance) * price ) - elapsed_time + size * 55
self.size = size
def size_up(self):
self.price += 100
self.size += 1
print(f'サイズが{self.size}になった!代わりに価格が高くなった...')
# 基底クラスのメソッドは呼び出されるので、冗長性がある
def drink(self):
super().drink()
print(f'サイズが{self.size}はちょっと多すぎたかな...')
# 実行結果 super()関数を使ったオーバーライド
super_override = SuperOverride('brown', 10, 200, 10, 300, 10)
super_override.drink() # <--- 満足度が4250点のホーフィーを飲む, サイズが11はちょっと多すぎたかな...
super_override.size_up() # <--- サイズが12になった!代わりに価格が高くなった...
SuperOverrideの__init__
メソッドのパラメータ(color, fragrance, price, taste, elapsed_time)に渡された値を基底(Coffee)クラスのパラメータに代入し初期化しています。super()で基底クラスのメソッドを丸々呼び出しているので、satisfactionも引き継がれています。
なお、super().drink()
はsuper(SuperOverride self).drink()
と同義です。
つまり、super(自クラス, self).メソッド名
の書くこともできますが、上記コードのように省略することもできます。
まとめ
以上のように、super()
は基底クラスのメソッドを継承した上で処理を拡張させたい時に使うことで、記述を節約してコードを見やすくすることができます。