一种为类型检查目的子类化 NamedTuple 的方法
A way to subclass NamedTuple for purposes of typechecking
我有几个共享某些字段的 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: Argument 1 to "DoSomething" has incompatible type "BaseExtended"; expected "Base"
有没有办法构建我的代码并保持 mypy 类型检查?我无法从 Base
继承 BaseExtended
,因为 NamedTuple
继承实现中的 there's a bug。
我也不想使用丑陋的 Union[Base, BaseExtended]
,因为当我尝试对 List
进行类型检查时这会中断,因为 List[Union[Base, BaseExtended]]
不等于 List[BaseExtended]
由于 some mypy magic about variant/covariant types.
我应该放弃这个想法吗?
有 PEP 544 提议对类型系统进行扩展,以允许结构子类型化(静态鸭子类型化)。 typing.NamedTuple
的运行时实现也将很快得到改进,可能在 6 月底的 Python 3.6.2 中(这也将通过 PyPI 上的 typing
向后移植)。
命名元组的构造方式使得从 typing.NamedTuple
classes 继承尚不可能。您必须编写自己的 metaclass 来扩展 typing.NamedTupleMeta
class 才能使 subclassing 工作,甚至 .
相反,您想使用新的 dataclasses
module 来定义您的 classes 并实现继承:
from dataclasses import dataclass
@dataclass(frozen=True)
class Base:
x: int
y: int
@dataclass(frozen=True)
class BaseExtended(Base):
z: str
该模块是 Python 3.7 中的新模块,但您可以 pip install dataclasses
the backport 在 Python 3.6 中使用。
上面定义了两个不可变的classes,具有x
和y
属性,BaseExtended
class又增加了一个属性。 BaseExtended
是 Base
的完整子 class,因此出于打字目的符合 DoSomething()
函数的要求。
classes 不是全名元组,因为它们没有长度或不支持索引,但是通过创建继承自 [=24= 的基 class 可以轻松添加], 添加两种方法通过索引访问字段。如果您将 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
MyPywill soon support dataclasses
explicitly;在 0.600 版本中,您仍然会遇到错误,因为它无法识别 dataclasses
模块导入或生成了 __new__
方法。
在Python 3.6及更早版本中,您也可以安装attrs
project来达到同样的效果;上面的序列基础 class 使用 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
.
生成的 classes
我有几个共享某些字段的 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: Argument 1 to "DoSomething" has incompatible type "BaseExtended"; expected "Base"
有没有办法构建我的代码并保持 mypy 类型检查?我无法从 Base
继承 BaseExtended
,因为 NamedTuple
继承实现中的 there's a bug。
我也不想使用丑陋的 Union[Base, BaseExtended]
,因为当我尝试对 List
进行类型检查时这会中断,因为 List[Union[Base, BaseExtended]]
不等于 List[BaseExtended]
由于 some mypy magic about variant/covariant types.
我应该放弃这个想法吗?
有 PEP 544 提议对类型系统进行扩展,以允许结构子类型化(静态鸭子类型化)。 typing.NamedTuple
的运行时实现也将很快得到改进,可能在 6 月底的 Python 3.6.2 中(这也将通过 PyPI 上的 typing
向后移植)。
命名元组的构造方式使得从 typing.NamedTuple
classes 继承尚不可能。您必须编写自己的 metaclass 来扩展 typing.NamedTupleMeta
class 才能使 subclassing 工作,甚至
相反,您想使用新的 dataclasses
module 来定义您的 classes 并实现继承:
from dataclasses import dataclass
@dataclass(frozen=True)
class Base:
x: int
y: int
@dataclass(frozen=True)
class BaseExtended(Base):
z: str
该模块是 Python 3.7 中的新模块,但您可以 pip install dataclasses
the backport 在 Python 3.6 中使用。
上面定义了两个不可变的classes,具有x
和y
属性,BaseExtended
class又增加了一个属性。 BaseExtended
是 Base
的完整子 class,因此出于打字目的符合 DoSomething()
函数的要求。
classes 不是全名元组,因为它们没有长度或不支持索引,但是通过创建继承自 [=24= 的基 class 可以轻松添加], 添加两种方法通过索引访问字段。如果您将 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
MyPywill soon support dataclasses
explicitly;在 0.600 版本中,您仍然会遇到错误,因为它无法识别 dataclasses
模块导入或生成了 __new__
方法。
在Python 3.6及更早版本中,您也可以安装attrs
project来达到同样的效果;上面的序列基础 class 使用 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
.