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?
Generic class parameters are specified in angle brackets “<>” after the class name as of the instance variable. Generic constructors are the same as generic methods. For generic constructors after the public keyword and before the class name the type parameter must be placed.
Generic functions: from typing import TypeVar, Sequence T = TypeVar('T') # Declare type variable def first(seq: Sequence[T]) -> T: return seq[0] def last(seq: Sequence[T]) -> T: return seq[-1] n = first([1, 2, 3]) # n has type int.
A generic interface is primarily a normal interface like any other. It can be used to declare a variable but assigned the appropriate class. It can be returned from a method. It can be passed as argument. You pass a generic interface primarily the same way you would an interface.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With