【问题标题】:What is the pythonic way of using interfaces - the abc way or the protocol way?使用接口的pythonic方式是什么——abc方式或协议方式?
【发布时间】:2021-07-24 14:05:36
【问题描述】:

考虑以下代码:


from typing import Protocol


class IDE(Protocol):
    def execute(self) -> None:
        ...


class Pycharm:
    def execute(self) -> None:
        print("Running pycharm")


class VsCode:
    def execute(self) -> None:
        print("Running VSCode")


class StringParser:
    def execute(self) -> None:
        print("Parsing string...")
        print("Done!")


class Foo:
    def bar(self) -> None:
        print("Hello")


class Code:
    def execute(self, ide: IDE) -> None:
        ide.execute()


def main() -> None:
    foo = Foo()
    sp = StringParser()
    vscode = VsCode()
    pycharm = Pycharm()
    code = Code()

    code.execute(pycharm)
    code.execute(vscode)
    code.execute(sp)
    # code.execute(foo)


if __name__ == "__main__":
    main()

这比使用名为IDE 的抽象基类然后在VsCodePycharm 中子类化它是否更具Python 风格?

据我了解,使用Protocol 的优点主要在于进行静态类型检查,而缺点主要是代码中的使用意图更加模糊。 另一方面,使用 ABC 方法确实可以明确告诉您哪个类应该工作,哪个不工作。此外,它还可以作为一个很好的文档。我不确定这有什么缺点,除了可能更冗长。我可以通过基类的类型提示来进行类型检查。在这两种情况下,StringParser 都会因为鸭子打字而起作用,在这种情况下,这有点不幸。

那么哪个更可取,也被认为更pythonic?

【问题讨论】:

  • 你对pythonic的定义是什么?
  • 我不认为这是其中之一。继承提供名义子类型,而Protocol 支持结构子类型。 ABC 不仅允许 重写继承的方法,而且要求 它,从而增加了名义子类型化。它以相当弱的形式这样做,因为关闭覆盖检查很容易。

标签: python python-3.x interface abstract-class


【解决方案1】:

您可能希望它继承两种类ProtocolABC

  1. 接口
  2. 具有一些具体方法和一些抽象方法的类

对于接口,我总​​是更喜欢Protocol 而不是ABC

PEP 可能难以阅读,但 CPython 代码清晰:

class _ProtocolMeta(ABCMeta):
    def __instancecheck__(cls, instance):
        ...

class Protocol(Generic, metaclass=_ProtocolMeta):
    __slots__ = ()
    _is_protocol = True
    _is_runtime_protocol = False

    def __init_subclass__(cls, *args, **kwargs):
        ...

class ABC(metaclass=ABCMeta):
    __slots__ = ()

如您所见,在运行时 Protocol 本质上是一个 ABC 子类,默认为 __instancecheck__。 所以@abstractmethodupdate_abstractmethods() 以及任何其他适用于ABC 的东西也适用于Protocol

继承协议类完全没问题。在下面的例子中,IDE 是一个协议,VsCode 是一个具体的类。它们通过 CPython 中的_is_protocol 类属性来区分。

class IDE(Protocol):
    ...

class VsCode(IDE):
    ...

在类型检查时,Protocol 显然更胜一筹。


对于“部分具体”的类,我宁愿避免使用ABC(或Protocol),因为它可能会产生误导。

人们很可能将ABC 子类误认为接口,因为:

  1. Python 标准库中的大多数ABC 子类都是接口:IterablePathLikeIOBase 等。
  2. PEP 主要是在谈论界面。

除非类必须在启动时强制覆盖,否则最好使用NotImplementedError

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-19
    • 1970-01-01
    • 1970-01-01
    • 2016-02-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多