Как создать универсальный c интерфейс в Python? - PullRequest
1 голос
/ 27 апреля 2020

Я хочу создать эквивалент этого в 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;
    }
}

Это моя попытка (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

... это не с main.py:11: error: Invariant type variable 'E' used in protocol where contravariant one is expected. Если только я не неправильно понял, Java, кажется, в порядке с инвариантом, и это тоже моя идея; Я не хочу, чтобы различные Filter были совместимы друг с другом. В любом случае, шлепок contravariant=True на T тоже не работает . Итак,

Зачем протоколу нужна контравариантная переменная? И как мне сделать эту Python проверку типа кода?

1 Ответ

2 голосов
/ 27 апреля 2020

Протоколы не позволяют этого, потому что это нарушает транзитивность подтипа. См. PEP 544 .

Если у вас есть следующие два класса:

class A:
    def method(self, arg: int):
        pass

class B(A):
    def method(self, arg: object):
        pass

, тогда B является допустимым подклассом A, потому что B.method может принять любые аргументы A.method может. Однако, если бы вы могли ввести следующий протокол:

T = typing.TypeVar('T')

class Proto(typing.Protocol[T]):
    def method(self, arg: T):
        pass

, тогда A удовлетворил бы Proto[int], а B - нет, из-за неизменности T.

...