Reputation: 5488
I want to create the equivalent of this in Python:
static class Event {}
static class MyEvent extends Event {}
interface Filter<E extends Event> {
boolean filter(E event);
}
static class MyFilter implements Filter<MyEvent> {
@Override public boolean filter(MyEvent event) {
return true;
}
}
This is my attempt (mypy-play):
from typing import TypeVar, Protocol
class Event:
pass
class MyEvent(Event):
pass
E = TypeVar("E", bound=Event)
class Filter(Protocol[E]):
def filter(self, event: E) -> bool:
raise NotImplementedError
class MyFilter(Filter):
def filter(self, event: MyEvent) -> bool: # should be ok
raise NotImplementedError
class BadFilter(Filter):
def filter(self, event: object) -> bool: # should fail
raise NotImplementedError
...that fails with main.py:11: error: Invariant type variable 'E' used in protocol where contravariant one is expected
. Unless I'm misunderstanding, Java seems to be fine with an invariant one, and this is my idea as well; I don't want various Filter
s to be compatible with one another. In any case, slapping contravariant=True
onto T
doesn't work either. So,
Why the protocol needs a contravariant variable? And, how do I make this Python code type check?
Upvotes: 10
Views: 13009
Reputation: 280973
Protocols don't allow that, because it breaks subtype transitivity. See PEP 544.
If you have the following two classes:
class A:
def method(self, arg: int):
pass
class B(A):
def method(self, arg: object):
pass
then B
is a valid subclass of A
, because B.method
can accept any arguments A.method
can. However, if you could introduce the following protocol:
T = typing.TypeVar('T')
class Proto(typing.Protocol[T]):
def method(self, arg: T):
pass
then A
would satisfy Proto[int]
, but B
would not, due to the invariance of T
.
Upvotes: 10