如何对mypy中继承自NamedTuple的类的方法实现类型检查?

时间:2017-03-03 20:33:54

标签: python typechecking namedtuple mypy

根据mypy文档,如果某个类需要引用自身,则可以使用forward-reference

这似乎适用于普通类,但是我无法使用继承自NamedTuple的类。

"""
All this code runs without error on Python 3.6 

The question is why the 'B' class' __add__ method
raises an error through mypy.
"""


from typing import *

class A:
    def __init__(self, x: int) -> None:
        self.x = x

    def __add__(self, other: 'A') -> 'A':
        return type(self)(self.x + other.x)

    def __str__(self) -> str:
        return f'A(x={self.x})'

A1 = A(1)
A2 = A(2)
A3 = A1 + A2
print(A3)

class B(NamedTuple('B', [('x', int)])):

    # The following line will raise an error in mypy
    # error: Argument 1 of "__add__" incompatible with supertype "tuple"
    def __add__(self, other: 'B') -> 'B':
        return type(self)(self.x + other.x)

B1 = B(1)
B2 = B(2)
B3 = B1 + B2
print(B3)

更新:Guido van Rossum自己回答this question on Github

  

我不是100%肯定你要完成的任务,但是根据你的初始例子,我猜你想重新定义+ B类,以便在B的实例上实现元素添加。原因mypy默认不支持这种情况称为“Liskov替换原则”(你可以谷歌解释)。

     

然而,有一种解决办法:将#type:忽略在产生错误的行上(def 添加行)。这不是合理的,但只要您从未将B实例传递给假定它是元组的代码并尝试在其上进行元组连接,它就会执行您想要的操作。

1 个答案:

答案 0 :(得分:1)

B.__add__的类型声明表明,将B的实例添加到另一个B实例是有效的(并且实现支持这个,因为它期望{{1} }} 上班)。但是,该方法会覆盖来自other.x(通过__add__)的更通用的tuple方法,该方法可以将任意两个元组连接在一起(因此右侧可以是一个实例namedtuple或任何tuple - 子类)。因为您的新方法对其参数有更严格的类型要求,所以您的类(从tuple的角度来看)不是mypy的正确子类。你不能将你的类的实例放到以前使用过元组的地方,并让它的工作方式相同。

考虑这个功能:

tuple

这适用于普通元组,但如果您将def tuple_append(tup: tuple, value: any) -> tuple: return tup1 + (value,) 个实例之一传递为B,它将失败(即使您传入的是tup子类的实例因为函数的类型声明需要)。这就是tuple不认为您的mypy类型有效的原因。如果它接受B,其他具有正确类型声明的代码可能会意外中断。

不幸的是,我认为围绕这​​个问题没有好办法。 Python没有其他语言所做的“私有继承”的概念。在没有公开成为子类的情况下,没有办法从另一个类继承实现。