Mypy 允许我们编写类存根,可以将其放置在与实际类相同的目录中。该存根与其他语言中已知的接口非常相似。是否可以让客户端使用存根并严格遵循存根实现?
我想工作的例子:
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.
Run Code Online (Sandbox Code Playgroud)
这有效。但是,如果DependencyImplementation不实现该do_something方法,则 Mypy 不会出现错误,Python 本身也不会出现错误。调用只是没有执行任何操作。我是否必须编写raise NotImplementedException()或注释每个方法@abc.abstractmethod才能使其工作?Mypy 或 Python 解释器中是否有一些特殊标志?
这是Mypy 协议的用例吗?它似乎即将推出(也许是 Python 4?)
这确实可以使用@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'
Run Code Online (Sandbox Code Playgroud)
关于这个例子有几点需要注意:
foo在第二个子类中进行定义。abc向 Parent 添加通常的元类(例如class Parent(metaclass=ABCMeta)):mypy 会理解@abc.abstractmethod有或没有它的含义。仅当您希望 Python 解释器也强制您已正确覆盖运行时标记为抽象的任何内容时,才包含元类。您还可以使用协议,但现在您需要首先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"
Run Code Online (Sandbox Code Playgroud)
一些注意事项:
Child故意不继承自CanFoo:类与其实现的协议之间没有显式链接:协议与 Go 风格的接口非常相似,并且可以更加临时。将此与 Java 等语言进行对比,在 Java 中,您确实需要在类定义中包含“implements Blah”。Child:它本身没有任何问题。相反,当我们尝试不正确地使用它时,我们会遇到异常。最后一些注意事项: