用于打字目的的命名为Tuple的一种方法



我有几个共享某些字段的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。

上面的 xy属性定义了两个不变的类, BaseExtended类添加了另外一个属性。BaseExtendedBase的完整子类,因此为键入目的适合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直接基于attrsattrs提供了更多功能。Mypy完全支持attrs生成的类。

有PEP 544提出了一个类型系统的扩展,该系统将允许结构性亚型(静态鸭键入(。同样,typing.NamedTuple的运行时实现将很快改善,可能在6月底3.6.2末(这也将通过PYPI上的typing进行备份(。

相关内容

最新更新