Consider the following code:
from typing import Union
class A:
def function_in_a(self) -> str:
return 'a'
class B:
def function_in_b(self) -> str:
return "b"
class C(A, B):
pass
def call_functions(c: Union[A, B]) -> None:
print(c.function_in_a())
print(c.function_in_b())
if __name__=="__main__":
c = C()
call_functions(c)
Note that the function call_functions
relies on definitions contained in both classes A
and B
. It expects objects that inherit from both of these classes.
This code will compile when run using python test.py
. But mypy --strict test.py
throws an error:
test.py:15: note: Revealed type is "Union[test.A, test.B]"
test.py:16: error: Item "B" of "Union[A, B]" has no attribute "function_in_a"
test.py:17: error: Item "A" of "Union[A, B]" has no attribute "function_in_b"
Found 2 errors in 1 file (checked 1 source file)
This makes sense to me. Union
means that c
can be a subclass of either A
or B
, but not both. I saw mention of an Intersection
type in PEP483 but a quick perusal of the typing
module docs showed that this type was never implemented.
How can I get mypy
to recognize that parameters of call_functions
are objects which inherit from both A and B using type hinting?
Use typing.Protocol
(New in version 3.8.) to define a type that must implement both methods invoked in the function.
from typing import Protocol
class A:
def function_in_a(self) -> str:
return 'a'
class B:
def function_in_b(self) -> str:
return "b"
class C(A, B):
pass
class D(B):
pass
class ProtoAB(Protocol):
def function_in_a(self) -> str:
...
def function_in_b(self) -> str:
...
def call_functions(obj: ProtoAB) -> None:
print(obj.function_in_a())
print(obj.function_in_b())
def main() -> None:
c = C()
call_functions(c)
d = D()
call_functions(d)
if __name__ == "__main__":
main()