sam*_*ser 2 python type-hinting python-3.x
我有一个定义接口的 python 类
\nclass InterfaceFoo:\n pass\nRun Code Online (Sandbox Code Playgroud)\n一些抽象类
\nclass AbstractBar:\n pass\nRun Code Online (Sandbox Code Playgroud)\n也许还有一个具体的类
\nclass Bar(InterfaceFoo):\n pass\nRun Code Online (Sandbox Code Playgroud)\n实现我的接口(或者,如果你愿意,接口Interface Foo和AbstractBar)。
AbstractBar现在,在某些情况下,我希望有一个类型提示,表示 \xc2\xbb 我期望派生自 \xc2\xab并实现\xc2\xab的类的实例InterfaceFoo。我怎样才能在Python中做到这一点?当然,我可以检查我的代码并添加所有可能的具体类,这些类是其子类AbstractBar并实现InterfaceFoo,但我认为这是一个非常丑陋的解决方案。
def func(obj: AbstractBar implementing InterfaceFoo):\n pass\nRun Code Online (Sandbox Code Playgroud)\n这在其他语言中是可能的,例如 Objective-C,您可以在其中编写BaseClass<Interface>. 我想知道Python 中是否有类似的东西?
解决方案确实是typing.Protocol. 然而,这里的挑战在于,您需要名义子类型(即声明为从另一个类型继承的类型)和结构子类型(即类型具有特定接口)的组合。
它Protocol的定义方式禁止在这里简单地使用多重继承,因为任何类型检查器都会很快告诉您,“协议的所有基础都必须是协议”。所以我们不能简单地继承 anyAbstractBar 和a Protocol。PEP 544的本节概述了有关此内容的详细信息。
然而,我们可以做的是将我们的抽象基类声明为一个协议。由于它是抽象的,因此不应该像协议一样直接实例化,因此这在大多数情况下应该有效。具体来说,对于abc标准库中的模块,我们可以选择指定抽象基类而不继承自,abc.ABC而是将元类设置为abc.ABCMeta。
看起来可能是这样的:
from abc import ABCMeta, abstractmethod
from typing import Protocol
class AbstractBase(Protocol, metaclass=ABCMeta):
@abstractmethod
def foo(self) -> int:
...
class SomeInterface(Protocol):
def bar(self) -> str:
...
class ABSomeInterface(AbstractBase, SomeInterface, Protocol):
pass
Run Code Online (Sandbox Code Playgroud)
我们现在可以定义一个函数,它期望其参数的类型ABSomeInterface如下:
def func(obj: ABSomeInterface) -> None:
print(obj.foo(), obj.bar())
Run Code Online (Sandbox Code Playgroud)
现在,如果我们想要实现一个具体的子类,AbstractBase并且我们希望该类符合我们的SomeInterface协议,我们还需要它实现一个bar方法:
class Concrete(AbstractBase):
def foo(self) -> int:
return 2
def bar(self) -> str:
return "x"
Run Code Online (Sandbox Code Playgroud)
Concrete现在我们可以安全地传递to的实例func并且类型检查器很高兴:
func(Concrete())
Run Code Online (Sandbox Code Playgroud)
相反,如果我们有另一个子类AbstractBase没有实现bar(因此没有遵循我们的SomeInterface协议),我们会得到一个错误:
class Other(AbstractBase):
def foo(self) -> int:
return 3
func(Other())
Run Code Online (Sandbox Code Playgroud)
mypy会这样抱怨:
error: Argument 1 to "func" has incompatible type "Other"; expected "ABSomeInterface" [arg-type]
note: "Other" is missing following "ABSomeInterface" protocol member:
note: bar
Run Code Online (Sandbox Code Playgroud)
这是我们所期望的,也是我们想要的。abc通过使用元类也保留了功能ABCMeta;AbstractBase因此,尝试在不实现的情况下进行子类化foo会在读取该模块时导致运行时错误。
为了完整起见,这里有一个完整的工作示例,其中SomeInterface是通用协议,以证明这也可以按预期工作:
error: Argument 1 to "func" has incompatible type "Other"; expected "ABSomeInterface" [arg-type]
note: "Other" is missing following "ABSomeInterface" protocol member:
note: bar
Run Code Online (Sandbox Code Playgroud)
值得注意的是,没有办法定义“纯粹”的SomeInterface协议。我们不能简单地拥有一个SomeInterface既可以实例化又可以将其用作协议的类。这就是这些事物的本质。
据我所知,Python 中尚不存在这样的交集类型,因此这种结构方法是我们能做的最好的方法。
| 归档时间: |
|
| 查看次数: |
1155 次 |
| 最近记录: |