Mypy使用重写方法报告不兼容的超类型错误

时间:2017-06-20 14:35:57

标签: python-3.x types mypy

以下是我在A.transform遇到的问题的简化示例。 B方法接受一个可迭代的对象,转换每个对象(在子类from typing import Iterable, TypeVar T = TypeVar('T') class A: def transform(self, x: Iterable[T]) -> Iterable[T]: raise NotImplementedError() class B(A): def transform(self, x: Iterable[str]) -> Iterable[str]: return [x.upper() for x in x] 中定义,并可能转换为其他子类)并返回一个可迭代的转换对象。

mypy

然而error: Argument 1 of "transform" incompatible with supertype "A" error: Return type of "transform" incompatible with supertype "A" 说:

[T]

如果我从A.transform()删除T = TypeVar('T', covariant=True),则错误就会消失。但这似乎是错误的解决方案。

在阅读covariance and contravariance之后,我想到了这个设定 <DataGrid Name="DataGridCamiao"> <!--columns, attributes, etc here--> </DataGrid> <TextBox Name="TextBoxMarca" Text="{Binding Path=SelectedItem.Marca, ElementName=DataGridCamiao}"/> 可能是一个解决方案,但这会产生相同的错误。

我该如何解决这个问题?我已经考虑完全对设计进行分区,并用更高阶函数替换A类。

1 个答案:

答案 0 :(得分:1)

在这种情况下,制作T协变或逆变并不能真正帮助你。假设mypy允许您在问题中使用的代码,并假设用户编写了以下代码片段:

def uses_a_or_subclass(foo: A) -> None:
    # This is perfectly typesafe (though it'll crash at runtime)
    print(a.transform(3))

# Uh-oh! B.transform expects a str, so we just broke typesafety!
uses_a_or_subclass(B())  

要记住的黄金法则是,当你需要覆盖或重新定义一个函数时(例如,当你正在进行子类化时),这些函数在参数中是逆变,并且返回类型中的协变。这意味着当您重新定义函数时,使参数更广泛/原始参数类型的超类是合法的,但不是子类型。

一种可能的解决方法是使整个班级T相关。然后,而不是子类化A(现在它等同于子类A[Any],如果你想保持完全类型安全,可能不是你想要的),你就是子类A[str]

现在,您的代码非常类型安全,重新定义的函数尊重函数方差:

from typing import Iterable, TypeVar, Generic

T = TypeVar('T')

class A(Generic[T]):
    def transform(self, x: Iterable[T]) -> Iterable[T]:
        raise NotImplementedError()

class B(A[str]):
    def transform(self, x: Iterable[str]) -> Iterable[str]:
        return [x.upper() for x in x]

现在,我们上面的uses_a_or_subclass函数应该被重写为通用的,或者专门接受子类型A[str]的类。无论哪种方式都有效,取决于你想要做什么。