What is the correct way to type hint such subclassed factories, like those in the following popular pattern? The Liskov Substitution Principle originally only applies to instances, not classes, so I don't understand why mypy seems to restrict the typing of class methods in subclasses.
from typing import Any, Self
class A:
def __init__(self, a: int):
self.a = a
@classmethod
def create(cls, a: int, **keywords: Any) -> Self:
return cls(a)
class B(A):
def __init__(self, a: int, b: int):
super().__init__(a)
self.b = b
@classmethod
def create(cls, a: int, b: int, **keywords: Any) -> Self:
return cls(a, b)
Running mypy (1.8.0) gives:
test_factory.py:19: error: Signature of "create" incompatible with supertype "A" [override]
test_factory.py:19: note: Superclass:
test_factory.py:19: note: @classmethod
test_factory.py:19: note: def create(cls, a: int, **keywords: Any) -> B
test_factory.py:19: note: Subclass:
test_factory.py:19: note: @classmethod
test_factory.py:19: note: def create(cls, a: int, b: int, **keywords: Any) -> B
Found 1 error in 1 file (checked 1 source file)
That would only be true if instances didn't have access to
@classmethods. The only thing that is allowed to violate LSP is__init__/__new__for practicality reasons, even though they are implicitly accessible on an instance (viatype(self)(<...>)). Nothing else, not even the "Pythonic"@classmethod-decorated alternative constructor methods like what you have here, is allowed to violate it.To solve this, you can loosely type the
@classmethodin the base class (mypy Playground):