Calling a superclass' factory method via super() infers the type of the superclass, rather than the type of the subclass.
# tmp.py
class A:
@classmethod
def new(cls) -> 'A':
return cls()
class B(A):
def print_foo(self):
print('foo')
@classmethod
def new(cls) -> 'B':
inst = super().new()
inst.print_foo()
return inst
assert isinstance(B.new(), B)
tmp.py:16: error: "A" has no attribute "print_foo"
tmp.py:17: error: Incompatible return value type (got "A", expected "B")
If attempting to specify the type of inst using inst = super().new() # type: 'B', mypy generates the following error:
tmp.py:15: error: Incompatible types in assignment (expression has type "A", variable has type "B")
Removing the type hint from the A.new gets rid of the errors.
PyLint also infers this type incorrectly, regardless of type hinting. See PyCQA/pylint#981 for details.
You should use a "self-type" in the signature of the new() method:
T = TypeVar('T')
class A:
@classmethod
def new(cls: Type[T]) -> T: ...
But how mypy would know that it is more precise if you annotated the return type of A.new as A? You should do it like this:
TA = TypeVar('TA', bound='A')
TB = TypeVar('TB', bound='B')
class A:
@classmethod
def new(cls: Type[TA]) -> TA:
return cls()
class B(A):
def print_foo(self):
print('foo')
@classmethod
def new(cls: Type[TB]) -> TB:
inst = super().new()
inst.print_foo()
return inst
Also, docs for this feature: http://mypy.readthedocs.io/en/latest/generics.html#generic-methods-and-generic-self
Most helpful comment
Also, docs for this feature: http://mypy.readthedocs.io/en/latest/generics.html#generic-methods-and-generic-self