環境
- Python 3.11.2
概要
abc.ABCを継承し忘れて、抽象基底クラスとして書いてしまうときがたまにあります。
ただその場合でも、問題なく動いていたりします。
abc.ABCを継承するとどうなるのか、なぜabc.ABCを継承する必要があるのかが分かっていなかったので、abc.ABCを継承する/しないによって挙動がどのように変わるかを調べました。
理想の状態
Dogクラスは具象クラスですが、cryメソッドの実装が漏れている状態です。
import abc
class Animal(abc.ABC):
@abc.abstractmethod
def cry(self) -> None:
pass
class Dog(Animal):
# def cry(self) -> None:
# print("bowwow")
def only_dog_method(self) -> None:
print("only dog method")
dog = Dog()
$ python sample.py
Traceback (most recent call last):
File "/home/vagrant/Documents/study/20230503/sample.py", line 18, in <module>
dog = Dog()
^^^^^
TypeError: Can't instantiate abstract class Dog with abstract method cry
$ mypy sample.py
sample.py:18: error: Cannot instantiate abstract class "Dog" with abstract attribute "cry" [abstract]
Found 1 error in 1 file (checked 1 source file)
python、mypy1でエラーが発生しました。
抽象メソッドは宣言しているが、abc.ABCを継承していない
import abc
class Animal:
@abc.abstractmethod
def cry(self) -> None:
pass
class Dog(Animal):
# def cry(self) -> None:
# print("bowwow")
def only_dog_method(self) -> None:
print("only dog method")
dog = Dog()
$ python sample1.py
$ mypy sample1.py
sample1.py:18: error: Cannot instantiate abstract class "Dog" with abstract attribute "cry" [abstract]
Found 1 error in 1 file (checked 1 source file)
python実行時にはエラーは発生しませんでした。
公式ドキュメントには、abc.abstractmethodについて以下のように記載されています。
このデコレータを使うには、クラスのメタクラスが ABCMeta かそれを継承したものである必要があります。 ABCMeta の派生クラスをメタクラスに持つクラスは、全ての抽象メソッドとプロパティをオーバーライドしない限りインスタンス化することができません。2
abc.ABCを継承していないクラスはabc.abstractmethodデコレータを使う条件を満たしていないため、Python実行時にエラーは発生しなかったようです。
mypyは、abc.abstractmethodを利用していれば抽象基底クラスだとみなして、エラーを出力しているのかもしれません。
抽象メソッドがないクラスでabc.ABCを継承する
Animalクラスをmixinクラスとして扱いたいです。Animalクラスはインスタンス化させたくありません。Animalは抽象クラスではありません(抽象メソッドが存在しない)が、abc.ABCを継承してみました。
import abc
class Animal(abc.ABC):
def cry(self) -> None:
print("cry, cry")
class Dog(Animal):
def only_dog_method(self) -> None:
print("only dog method")
animal = Animal() # インスタンス化を禁止させたい
dog = Dog()
$ python sample2.py
$ mypy sample2.py
Success: no issues found in 1 source file
python, mypyでエラーは発生しませんでした。
そもそもDogもabc.ABCのサブクラスなので、abc.ABCを継承したらインスタンス生成を禁止できる訳がないですね。。。
issubclass(Dog, abc.ABC) # => True
issubclass(Animal, abc.ABC) # => True
-
mypyはインスタンス化しないと、抽象メソッドの実装漏れのエラーは発生しません。https://qiita.com/yuji38kwmt/items/1000adfadc7faea7d0ea ↩
-
https://docs.python.org/ja/3.11/library/abc.html#abc.abstractmethod ↩