a_g*_*est 5 python static-typing interface python-3.x mypy
我有两个基本类Foo和Bar,以及一个Worker期望行为类似的类Foo。然后,我添加了另一个实现所有相关属性和方法的类,Foo但是我没有设法通过mypy将其成功传递给静态类型检查。这是一个小例子:
class MyMeta(type):
pass
class Bar(metaclass=MyMeta):
def bar(self):
pass
class Foo:
def __init__(self, x: int):
self.x = x
def foo(self):
pass
class Worker:
def __init__(self, obj: Foo):
self.x = obj.x
Run Code Online (Sandbox Code Playgroud)
这里Worker实际上接受任何Fooish对象,即具有属性x和方法的对象foo。因此,如果obj走路像a一样Foo,如果它像a一样嘎嘎作响,Foo那么Worker它将很高兴。现在,整个项目都使用类型提示,因此暂时指示obj: Foo。到目前为止,一切都很好。
现在有另一个类FooBar,该类具有子类Bar和行为,Foo但不能子类化,Foo因为它通过属性公开其属性(因此__init__参数没有意义):
class FooBar(Bar):
"""Objects of this type are bar and they are foo-ish."""
@property
def x(self) -> int:
return 0
def foo(self):
pass
Run Code Online (Sandbox Code Playgroud)
在这一点上,Worker(FooBar())显然会导致类型检查器错误:
error: Argument 1 to "Worker" has incompatible type "FooBar"; expected "Foo"
Run Code Online (Sandbox Code Playgroud)
为了将Foo-ish 的接口传达给类型检查器,我考虑过为Foo-ish类型创建抽象基类:
import abc
class Fooish(abc.ABC):
x : int
@abc.abstractmethod
def foo(self) -> int:
raise NotImplementedError
Run Code Online (Sandbox Code Playgroud)
但是我不能FooBar从它继承,Fooish因为Bar它有自己的元类,因此会引起元类冲突。所以我考虑过Fooish.register在两者上使用Foo,FooBar但是mypy并不同意:
@Fooish.register
class Foo:
...
@Fooish.register
class FooBar(Bar):
...
class Worker:
def __init__(self, obj: Fooish):
self.x = obj.x
Run Code Online (Sandbox Code Playgroud)
产生以下错误:
error: Argument 1 to "Worker" has incompatible type "Foo"; expected "Fooish"
error: Argument 1 to "Worker" has incompatible type "FooBar"; expected "Fooish"
Run Code Online (Sandbox Code Playgroud)
我考虑的下一个选项是创建一个接口,而无需abc.ABC以“常规”类的形式继承,然后同时拥有Foo和FooBar继承它:
class Fooish:
x : int
def foo(self) -> int:
raise NotImplementedError
class Foo(Fooish):
...
class FooBar(Bar, Fooish):
...
class Worker:
def __init__(self, obj: Fooish):
self.x = obj.x
Run Code Online (Sandbox Code Playgroud)
现在,mypy不再抱怨的参数类型,Worker.__init__而是抱怨FooBar.x(是property)与Fooish.x以下项的签名不兼容:
error: Signature of "x" incompatible with supertype "Fooish"
Run Code Online (Sandbox Code Playgroud)
而且,Fooish(抽象)基类现在可以实例化,并且是有效的参数,Worker(...)尽管它没有提供属性,但没有意义x。
现在,我陷入了一个问题,即如何在不使用继承的情况下将该接口与类型检查器进行通信(由于元类冲突;即使有可能,mypy仍会抱怨的签名不兼容x)。有办法吗?
PEP 544添加了对结构子类型的支持- 协议:从 Python 3.8 开始,结构子类型(静态鸭子类型) 。对于 3.8 之前的版本,PyPI 上的Typing-extensions包提供了相应的实现。
typing.ProtocolPEP更详细地解释了与所讨论的场景相关的内容。这允许定义隐式子类型,从而使我们免于元类冲突问题,因为不需要继承。所以代码看起来像这样:
from typing import Protocol # Python 3.8+
from typing_extensions import Protocol # Python 3.5 - 3.7
class Fooish(Protocol):
x : int
def foo(self) -> int:
raise NotImplementedError
# No inheritance required, implementing the defined protocol implicitly subtypes 'Fooish'.
class Foo:
def __init__(self, x: int):
self.x = x
def foo(self):
pass
class MyMeta(type):
pass
class Bar(metaclass=MyMeta):
def bar(self):
pass
# Here, we again create an implicit subtype of 'Fooish'.
class FooBar(Bar):
"""Objects of this type are bar and they are foo-ish."""
@property
def x(self) -> int:
return 0
@x.setter
def x(self, val):
pass
def foo(self):
pass
class Worker:
def __init__(self, obj: Fooish):
self.x = obj.x
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
79 次 |
| 最近记录: |