環境
- Python 3.9.7
- mypy 0.920
やりたいこと
データクラスである抽象基底クラスAnimal
を作りたいです。
import abc
from dataclasses import dataclass
@dataclass(frozen=True)
class Animal(abc.ABC):
name: str
@abc.abstractmethod
def cry(self):
pass
@dataclass(frozen=True)
class Dog(Animal):
color: str
def cry(self):
print("ワンワン")
@dataclass(frozen=True)
class Bird(Animal):
can_fly: bool
def cry(self):
print("ガーガー")
def main():
dog = Dog(name="alice", color="blue")
dog.cry()
bird = Bird(name="bob", can_fly=False)
bird.cry()
if __name__ == "__main__":
main()
$ python foo.py
ワンワン
ガーガー
mypyのエラーメッセージ
mypyで型チェックしたところ、以下のエラーメッセージが表示されました。
$ mypy foo.py
foo.py:5: error: Only concrete class can be given where "Type[Animal]" is expected
Found 1 error in 1 file (checked 1 source file)
原因
Stack OverFlowの質問によると、mypyのバグのようです。
I gather from mypy issue #5374 that this is a bug in mypy, first noticed in 2018 and still not corrected.
暫定的な解決方法
AnimalDataclassMixin
クラスをデータクラスをmix-inクラスとして定義して、Animal
クラスはそれを継承するようにしました。
上記のStack Overflowの回答通りの方法です。
@dataclass(frozen=True)
class AnimalDataclassMixin:
name: str
class Animal(abc.ABC, AnimalDataclassMixin):
@abc.abstractmethod
def cry(self):
pass
補足
mix-inクラスなのにメソッドが明示的に定義されていないのは、少し混乱するかもしれませんね。
以下、mix-inクラスの定義。
MixinまたはMix-in(ミックスイン)は[1][2][3][4]、オブジェクト指向プログラミングで用いられる技法であり、他のクラスから使用されるメソッド群を持つクラスが、他のクラスのスーパークラスにならないで済むための、特別な多重継承関係を実現するためのメカニズムを意味している。Mix-inされたメソッドに、他のクラスがアクセスする方法はそれぞれの言語仕様に依存している。
#type: ignore
で型チェックを無視するよりは良い方法ですが。