Pythonの@typing.overrideの役割と使い方
役割
子クラスが親クラスのメソッドをオーバーライドしている場合、親クラス側でそのメソッドが変更されると、子クラス側も同様に修正が必要になります。しかし従来の仕組みではその関係を型チェッカーが検知できませんでした。そこで、この問題を解決するためにPython3.12で@typing.override
(以下、@override
)が導入されました。
@override
を使うことで、親メソッドとメソッド名が異なる場合やシグネチャが異なる場合に自動で検知できます。(実は@override
がなくても検知できるケースもあるので後述します。)
使い方
基本例
親クラスのメソッドをオーバーライドしているメソッドにデコレーターとして追加するだけです。
from typing_extensions import override
class Base:
...
def show(self, num: int):
print(num)
class Sub(Base):
...
@override
def show(self, num: int):
print(num * 2)
if __name__ == "__main__":
sub = Sub()
sub.show(2)
% mypy .
Success: no issues found in 1 source file
関数名が異なるケース
ここで、ある人が以下のようにBase
クラスのshow
メソッドのメソッド名をdisplay
に変更したとします。
class Base:
...
def display(self, num: int):
print(num)
型チェックを実行すると以下のように検出することができます。
% mypy .
example.py:14: error: Method "show" is marked as an override, but no base method was found with this name [misc]
Found 1 error in 1 file (checked 1 source file)
ここで試しに子クラスの@override
をコメントアウトしてみます。
この場合、Sub
クラスのshow
メソッドはオーバーライドではなく新しいクラスの定義と解釈されるため、特にエラーが出ることはありません。
class Sub(Base):
...
# @override
def show(self, num: int):
print(num * 2)
% mypy .
Success: no issues found in 1 source file
無事(?)検出されなくなりました。
関数のシグネチャが異なるケース
続いて先ほどの関数名を下に戻した上で、引数num
の型をint
からstr
へ変更してみます。
class Base:
...
def show(self, num: str):
return num
% mypy .
example.py:14: error: Argument 1 of "show" is incompatible with supertype "Base"; supertype defines the argument type as "str" [override]
(中略)
Found 1 error in 1 file (checked 1 source file)
検知できています。
こちらでも@override
をコメントアウトしてみると、結果は以下のようになります。
% mypy .
example.py:17: error: Argument 1 of "show" is incompatible with supertype "Base"; supertype defines the argument type as "str" [override]
(中略)
Found 1 error in 1 file (checked 1 source file)
なぜかきちんと検知できています。
実はmypy側でオーバーライドしているかをチェックして、親クラスと子クラスのメソッドのシグネチャに差分があるときは検出してくれるようになっています。
さいごに
mypyなどの型チェックツールである程度よしなにやってくれてるとはいえ、以下の理由で依然として@override
を使うメリットはあるのかなと思います。
- 親にそのメソッドが本当に存在するかを保証できる
- 可読性が上がる
参考文献
Discussion