Reputation: 445
I have a setup that looks as following:
from typing import TypeVar
MyType = TypeVar('MyType', bound='TestClass')
class TestClass():
def __init__(self, number):
self.number = number
def __mul__(self, multiplicator: int) -> 'TestClass':
return TestClass(self.number*multiplicator)
def test_mul(self: MyType, multiplicator: int) -> MyType:
return self*multiplicator
class InheritingClass(TestClass):
def __mul__(self, multiplicator: int) -> 'InheritingClass':
return InheritingClass(self.number*multiplicator+1)
Evaluating this with mypy returns the error Incompatible return value type (got "TestClass", expected "MyType")
in the line return self*multiplicator
in TestClass.test_mul
The goal here is to ultimately have two different objects, where one inherits the properties of the other, and have them use the same test_mul
function while making use of a different __mul__
function. I want to be able to call the test_mul
function on either class, with the respective __mul__
functions of each class being called accordingly. Is there a way to achieve that without altering the way a new instance of the own class is returned in each __mul__
function? If not, how would an alternative look here?
Upvotes: 0
Views: 485
Reputation: 1920
The problem is in __mul__
in TestClass
: it should return MyType
and use type(self)
.
MyType = TypeVar('MyType', bound='TestClass')
class TestClass():
def __init__(self, number):
self.number = number
def __mul__(self: MyType, multiplicator: int) -> MyType:
return type(self)(self.number * multiplicator)
def test_mul(self: MyType, multiplicator: int) -> MyType:
x: MyType = self * multiplicator
return x
class InheritingClass(TestClass):
def __mul__(self, multiplicator: int) -> 'InheritingClass':
return InheritingClass(self.number * multiplicator + 1)
tc = TestClass(1)
tcx = tc * 3
tcy = tc.test_mul(4)
if TYPE_CHECKING:
# Mypy: Revealed type is "Tuple[TestClass*, TestClass*]"
reveal_type((tcx, tcy))
ic = InheritingClass(2)
icx = ic * 3
icy = ic.test_mul(4)
if TYPE_CHECKING:
# Mypy: Revealed type is "Tuple[InheritingClass, InheritingClass*]"
reveal_type((icx, icy))
Upvotes: 1
Reputation: 839
A cast to MyType
in the test_mul
method should work:
from typing import TypeVar, cast
MyType = TypeVar('MyType', bound='TestClass')
class TestClass():
def __init__(self, number):
self.number = number
def __mul__(self, multiplicator: int) -> 'TestClass':
return TestClass(self.number*multiplicator)
def test_mul(self: MyType, multiplicator: int) -> MyType:
return cast(MyType, self*multiplicator)
class InheritingClass(TestClass):
def __mul__(self, multiplicator: int) -> 'InheritingClass':
return InheritingClass(self.number*multiplicator+1)
reveal_type(TestClass(5).test_mul(5))
reveal_type(InheritingClass(5).test_mul(5))
gives us
file.py:21: note: Revealed type is "file.TestClass*"
file.py:22: note: Revealed type is "file.InheritingClass*"
When mypy support for it comes, using the Self type (planned for 3.11, currently in typing-extensions
) as the return annotation for the test_mul
method should also work. The current PR that plans to add is can be found here https://github.com/python/mypy/pull/11666
Upvotes: 3