Python 一种用于类型检查的NamedTuple子类的方法

Python 一种用于类型检查的NamedTuple子类的方法,python,typing,namedtuple,mypy,Python,Typing,Namedtuple,Mypy,我有几个共享一些字段的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):

我有几个共享一些字段的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:error:DoSomething的参数1已被删除 不兼容类型“BaseExtended”;预期“基数”

是否没有办法构造我的代码并保持mypy类型检查?我无法从Base继承BaseExtended,因为NamedTuple继承实现中存在错误:

我也不想使用难看的“Union[Base,BaseExtended]”,因为当我尝试对列表进行打字检查时,这会中断,因为“List[Union[Base,BaseExtended]]”与“List[BaseExtended]”不相等,这是因为mypy在变量/协变类型方面的魔力:


我应该放弃这个想法吗?

有人建议对类型系统进行扩展,允许结构子类型(静态鸭式类型)。另外,
typing.NamedTuple
的运行时实现也将很快得到改进,可能在6月底的Python 3.6.2中(这也将通过PyPI上的
typing
进行后端口)。

命名元组的构造方式使得继承
typing.NamedTuple
类成为可能。您必须编写自己的元类来扩展
typing.NamedTupleMeta
类,以使子类化工作正常,即使这样

相反,您希望使用新的定义类并实现继承:

from dataclasses import dataclass

@dataclass(frozen=True)
class Base:
    x: int
    y: int

@dataclass(frozen=True)
class BaseExtended(Base):
    z: str
该模块在Python3.7中是新的,但是您可以在Python3.6上使用它

上面定义了两个具有
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;在版本0.600中,由于无法识别
数据类
模块导入,或者生成了
\uuuu新方法,因此仍然会出现错误

在Python3.6和更早版本中,您还可以安装以实现相同的效果;使用
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
生成的类,谢谢。我猜在3.6.2之前,你不知道我可以使用什么优雅的变通方法?如果是这样的话,我就接受答案,继续前进。@wuzwm不,不幸的是我不知道。改进还没有实现,同样的问题仍然适用于Python 3.6.5和3.7.0b4。实际上,运行时更改是故意放弃的。决定命名元组应保持最低限度,对于所有复杂用例,用户应使用数据类。