【问题标题】:How do I create a generic interface in Python?如何在 Python 中创建通用接口?
【发布时间】:2020-04-27 20:13:25
【问题描述】:

我想在 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 似乎可以使用不变量,这也是我的想法;我不希望各种Filters 相互兼容。无论如何,将contravariant=True 打到T doesn't work either 上。所以,

为什么协议需要逆变变量?还有,我如何进行这个 Python 代码类型检查?

【问题讨论】:

  • 当我点击“运行”时,您的“也不起作用”链接显示“成功:在 1 个源文件中未发现问题”。
  • 哦,等等,你以为 BadFilter 会失败。逆变意味着BadFilter 很好。可以过滤任意对象的过滤器可以过滤事件。
  • @user2357112supportsMonica MyFilter 过滤子类,BadFilter 过滤超类。肯定其中之一应该失败?

标签: python python-typing


【解决方案1】:

协议不允许这样做,因为它破坏了子类型的传递性。见PEP 544

如果你有以下两个类:

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

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

那么BA 的有效子类,因为B.method 可以接受任何参数A.method 可以。但是,如果您可以引入以下协议:

T = typing.TypeVar('T')

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

那么A 会满足Proto[int],但B 不会,因为T 的不变性。

【讨论】:

  • 我想如果遵循我的 Java 想法,也就是说,如果 A 明确地实现了协议,那么将其子类化将是一个错误,并且不会出现此问题。我想我想要么使用Protocol 以外的其他东西,要么改用逆变器TypeVar。这是我最新的attempt at the latter——看起来不错?
  • @squirrel:乍一看似乎很好。 foo: Filter = BadFilter2() 通过,因为Foo 被解释为Foo[Any],而Any 基本上是“不要检查我”类型。这有点像在 Java 中使用原始类型。
  • 啊哈,谢谢。知道为什么它被解释为Filter[Any] 而不是定义的Filter[E] 吗?我不确定它是否像在 Java 中一样工作,因为 Java 需要显式声明接口并且根本不允许我制作像 BadFilter2 这样的东西
猜你喜欢
  • 2019-09-03
  • 2016-04-13
  • 1970-01-01
  • 2011-12-19
  • 1970-01-01
  • 2014-09-23
  • 2023-03-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多