我有几个共享某些字段的namedtuple
。我的功能可以接受这些元组,并且可以保证仅与共享字段相互作用。我想在mypy中打字。
代码的一个示例是:
from typing import NamedTuple
class Base(NamedTuple):
x: int
y: int
class BaseExtended(NamedTuple):
x: int
y: int
z: str
def DoSomething(tuple: Base):
return tuple.x + tuple.y
base = Base(3, 4)
base_extended = BaseExtended(5, 6, 'foo')
DoSomething(base)
DoSomething(base_extended)
当我在此代码上运行mypy时,我会得到一个可预测的错误:
mypy_example.py:20:错误:参数1 to'dosomething&quot&quot&quot具有不兼容的类型" baseextended";预期的"基础"
是否没有办法构建我的代码并保持mypy typechecking?我无法从Base
继承BaseExtended
,因为NamedTuple
继承实现中有一个错误。
我也不想使用丑陋的 Union[Base, BaseExtended]
,因为当我尝试键入 List
时,这会破裂,因为 List[Union[Base, BaseExtended]]
由于某些关于变体/协变量类型的mypy魔法而不等于 List[BaseExtended]
。
我应该放弃这个主意吗?
构造命名元组的方式使得无法从typing.NamedTuple
类中继承。您必须编写自己的元素来扩展typing.NamedTupleMeta
类以进行子类别工作,即使是collections.namedtuple()
生成的类也不是为了扩展。
相反,您要使用新的dataclasses
模块来定义您的类并实现继承:
from dataclasses import dataclass
@dataclass(frozen=True)
class Base:
x: int
y: int
@dataclass(frozen=True)
class BaseExtended(Base):
z: str
该模块在python 3.7中是新的,但是您可以在python 3.6上进行 pip install dataclasses
backport。
上面的 x
和 y
属性定义了两个不变的类, BaseExtended
类添加了另外一个属性。BaseExtended
是Base
的完整子类,因此为键入目的适合DoSomething()
函数的要求。
这些类并不完整,因为它们没有长度或支持索引,但是通过创建从collections.abc.Sequence
继承的基本镜来添加这是通过索引添加两种方法来添加的。如果将order=True
添加到@dataclass()
装饰器中,那么您的实例以相同的方式(命名(元组完全订购:
from collections.abc import Sequence
from dataclasses import dataclass, fields
class DataclassSequence(Sequence):
# make a dataclass tuple-like by accessing fields by index
def __getitem__(self, i):
return getattr(self, fields(self)[i].name)
def __len__(self):
return len(fields(self))
@dataclass(frozen=True, order=True)
class Base(DataclassSequence):
x: int
y: int
mypy很快将明确支持dataclasses
;在版本0.600中,您仍然会遇到错误,因为它无法识别dataclasses
模块导入或生成__new__
方法。
在Python 3.6及更早之前,您还可以安装attrs
项目以实现相同的效果;上面的序列基类使用attrs
:
from collections.abc import Sequence
import attr
class AttrsSequence(Sequence):
# make a dataclass tuple-like by accessing fields by index
def __getitem__(self, i):
return getattr(self, attr.fields(type(self))[i].name)
def __len__(self):
return len(attr.fields(type(self)))
@attr.s(frozen=True, auto_attribs=True)
class Base(AttrsSequence):
x: int
y: int
dataclasses
直接基于attrs
,attrs
提供了更多功能。Mypy完全支持attrs
生成的类。
有PEP 544提出了一个类型系统的扩展,该系统将允许结构性亚型(静态鸭键入(。同样,typing.NamedTuple
的运行时实现将很快改善,可能在6月底3.6.2末(这也将通过PYPI上的typing
进行备份(。