我可以使用Mypy存根作为接口吗?

时间:2019-05-10 06:04:03

标签: python mypy

Mypy允许我们编写class stubs,可以将其放置在与实际类相同的目录中。此存根与其他语言中的接口非常相似。客户端可以使用存根,并且实现严格遵循存根吗?

我想工作的示例:

class IDependency:
  def do_something(self) -> None: ...
  def do_something_else(self) -> None: ...

class Service:
  def __init__(self, dependency: IDependency):
    dependency.do_something()
    dependency.do_something_else() # this fails silently

class DependencyImplementation(IDependency):
  def do_something(self) -> None:
    print("doing something")

  # Note there is no `do_something_else` here.

这有效。但是,如果DependencyImplementation未实现do_something方法,则Mypy不会出错,Python本身也不会出错。该呼叫只是不执行任何操作。我是否必须编写raise NotImplementedException()或用@abc.abstractmethod注释每个方法才能起作用? Mypy或Python解释器中是否有一些特殊标志?

这是Mypy Protocols的用例吗?它似乎即将到来(也许是Python 4?)

1 个答案:

答案 0 :(得分:1)

这确实可以使用@abc.abstractmethod或协议来完成。前者类似于使用Java的抽象类。后者将类似于使用Go的界面或Rust特征。

以下是使用ABC的示例:

from abc import abstractmethod

class Parent:
    @abstractmethod
    def foo(self) -> None: ...

# Missing an implementation for 'foo'!
class Child(Parent): pass

print(Child())  # Error: Cannot instantiate abstract class 'Child' with abstract attribute 'foo'

关于此示例的一些注意事项:

  1. 您在Child类的实例而不是声明上遇到错误。这是为了支持您从不实例化Child而是再次对其进行子类化并在该第二个子类中定义foo的用例。
  2. 我们不需要将常见的abc元类添加到Parent(例如class Parent(metaclass=ABCMeta)):mypy会理解@abc.abstractmethod在有或没有它的含义。仅当您希望Python解释器也强制您正确重写了在运行时标记为抽象的所有内容时,才包含元类。
  3. ABC并不是完全的接口-您仍然可以定义字段和非抽象方法。它们更类似于Java风格的抽象类。

您也可以使用协议,尽管现在您需要先pip install typing_extensions才能使用它。这是一个示例:

from typing_extensions import Protocol

class CanFoo(Protocol):
    def foo(self) -> None: ...

class Child: pass

def expects_fooable(x: CanFoo) -> None: ...

x = Child()
expects_fooable(x)  # Error: Argument 1 to "expects_fooable" has incompatible type "Child"; expected "CanFoo"

一些注意事项:

  1. 在这里,Child有意不继承自CanFoo:类与它实现的协议之间没有明确的链接:协议与Go风格的接口非常相似,可以更特别。将此与Java之类的语言进行对比,您确实需要在类定义中包括“ implements Blah”。
  2. 与先前的错误不同,我们在Child的实例化上没有得到错误:它本质上没有错。相反,当我们尝试不正确使用它时,我们会得到一个例外。

最后几点注意事项:

  1. 存根文件可能从表面上看起来像接口,但实际上并非如此:它们只是将类型带入代码的一种方式,我们无法轻易对其进行修改并向其添加类型提示。您可以认为它们与C样式的头文件大致相似:这是一种独立于源代码存储现有对象签名的方法。
  2. “ Typeshed”是specific project的名称,其中包括标准库的存根和一些流行的第三方模块。该词不是“存根文件”的同义词。同样,术语“类存根”也有点用词不当:只有存根文件,该文件可能包含也可能不包含类的定义。 (如果您尝试键入的原始Python或C扩展库仅包含函数,则相应的存根文件也可能仅包含这些函数的签名。)