Search code examples
pythonmypy

Why is mypy trying to instantiate my abstract class in Python?


If I have a Python module like this:

from abc import ABC, abstractmethod

class AbstractClass(ABC):
    @abstractmethod
    def method(self):
        pass

class ConcreteClass1(AbstractClass):
    def method(self):
        print("hello")

class ConcreteClass2(AbstractClass):
    def method(self):
        print("hello")

class ConcreteClass3(AbstractClass):
    def method(self):
        print("hello")

classes = [
    ConcreteClass1,
    ConcreteClass2,
    ConcreteClass3,
]

for c in classes:
    c().method()

And I hit it with mypy test.py, I get this:

test.py:27: error: Cannot instantiate abstract class "AbstractClass" with abstract attribute "method"

The code runs without any issues though, and I can't see any issues in the logic. At no point am I trying to instantiate the AbstractClass directly.

Some weird behavior I've noticed:

If, instead of a loop, I do this:

...
ConcreteClass1().method()
ConcreteClass2().method()
ConcreteClass3().method()

mypy is happy.

Also, if, instead of 3 classes in the loop, I do 2:

classes = [
    ConcreteClass1,
    ConcreteClass2,
    #ConcreteClass3,
]

for c in classes:
    c().method()

mypy is happy with that as well. What's going on? Is this a mypy bug? If so, can I tell mypy to ignore this "problem"?


Solution

  • The problem appears similar to mypy issues with abstract classes and dictionaries - for some reason, mypy can't typecheck this properly without a type annotation on the list:

    classes: list[Type[AbstractClass]] = [
        ConcreteClass1,
        ConcreteClass2,
        ConcreteClass3,
    ]
    

    (Change list to List if you're on Python 3.8 or below)

    You might want to file an issue for this bug on the mypy Github.