Reputation: 400
This question is to clarify my doubts related to python typing
from typing import Union
class ParentClass:
parent_prop = 1
class ChildA(ParentClass):
child_a_prop = 2
class ChildB(ParentClass):
child_b_prop = 3
def method_body(val) -> ParentClass:
if val:
return ChildA()
else:
return ChildB()
def another_method() -> ChildA:
return method_body(True)
print(another_method().child_a_prop)
In the above piece of code, the linting tool I used is printing error as below
error: Incompatible return value type (got "ParentClass", expected "ChildA")
(where I do method_body(True))
I have also set the method_body
return type as Union[ChildA, ChildB]
.
This will result error: Incompatible return value type (got "Union[ChildA, ChildB]", expected "ChildA")
I am looking for a better way to do this. If anyone knows the solution, your help will be very much appreciated.
Upvotes: 4
Views: 1831
Reputation: 3920
mypy does not do runtime analysis so it cannot guess that calling
method_body
with argument True
will always result in a ChildA
object. So the error it produces does make sense.
You have to guide mypy in some way to tell him that you know what you are doing and that another_method
indeed produces a ChildA
object when called with argument True
. One is to use a cast
:
from typing import cast
def another_method() -> ChildA:
return cast(ChildA, method_body(True))
Another one is to add an assertion:
def another_method() -> ChildA:
result = method_body(True)
assert isinstance(result, ChildA)
return result
The difference between the two is that the cast
does not have any runtime implication. You can think of it as a comment put here to guide mypy in its checks, but the cast
function only returns its second parameter, ie, here is the body of cast
:
def cast(typ, val):
return val
Whereas the assert
can naturally raise an AssertionError
error (not in that case obviously, but in general).
Upvotes: 6
Reputation: 14434
Naturally ParentClass
isn't compatible with ChildA
since not all ParentClass
instances will be ChildA
. The same goes for Union[ChildA, ChildB]
.
In order for another_method
to pass type checking, we need to provide the type checker with additional information about the runtime types involved, namely that method_boy(True)
will always return a ChildA
, not just a ParentClass
. This can be accomplished using typing.cast
:
def another_method() -> ChildA:
return typing.cast(ChildA, method_body(True))
Upvotes: 3