mypy 使用 child 的名称而不是 parent 方法的通用签名从 child 方法更新 return 值

mypy updating return value from child method using child's name instead of parent's method's generic signature

我有一个 Generic 基础 class,它 return 本身就是一种方法 (get_self)。我已经打字提示了。

然后我有一个 child class 的基数 class,它为 Generic 传入一个类型参数。在childclass中,我调用get_self。我想将类型提示更新为 child class.

的名称

但是,mypy==0.782 正在报告 error: Incompatible return value type (got "Foo[Bar]", expected "DFoo") [return-value]。有什么办法可以做到这一点吗?


**编辑**

经过深思熟虑,我决定re-explain这个问题。提前抱歉冗长。

  1. Base class (Foo) 有一个方法 (get_self) 类型提示 return 自身的一个实例
  2. Child class (DFoo) 不覆盖方法
  3. Child class 然后使用 (get_self) 方法
    • 并且知道 return 类型实际上是 child class (DFoo)
  4. 然而,静态类型检查器(例如:mypy)并不知道 child class 的方法实际上 return 一个 object child class 中的,因为他们正在使用基础 class
  5. 中的类型提示

所以如果没有 re-declaring child class 中的方法 (get_self) 和新类型提示,我的问题可能无法实现。

我可以将 get_self 的 return 设为 TypeVar。但是,由于基础 class Foo 已经是 Generic,目前这是不可能的,因为它需要 python/typing Higher-Kinded TypeVars #548 中提到的“Higher-Kinded TypeVars”。


示例脚本

我希望这能澄清我想要表达的意思。

from __future__ import annotations

from typing import Generic, TypeVar, cast

T = TypeVar("T")

class Foo(Generic[T]):
    def get_self(self) -> Foo[T]:
        # Other stuff happens here before the return
        return self

class Bar:
    pass

class DFoo(Foo[Bar]):
    def do_something_get_self(self) -> DFoo:
        # mypy error: Incompatible return value type (got "Foo[Bar]", 
        # expected "DFoo")
        return self.get_self()

class DFooCast(Foo[Bar]):
    def do_something_get_self(self) -> DFooCast:
        # This works, but I don't like this method. I don't want to use `cast`
        # all over the place.
        return cast(DFooCast, self.get_self())

class DFooNoUpdatedTypeHint(Foo[Bar]):
    def do_something_get_self(self) -> Foo[Bar]:
        # mypy doesn't error here, but later on it will raise an error 
        # when using method's added in Foo subclasses
        return self.get_self()

    def dfoo_adds_method(self) -> None:
        """DFoo also has additional methods."""

dfoo = DFooNoUpdatedTypeHint()
dfoo.do_something_get_self().dfoo_adds_method()  # error: "Foo[Bar]" has no attribute "dfoo_adds_method"

这是完整的 mypy 输出:

path/to/ret_type_type_t_subclass.py: note: In member "do_something_get_self" of class "DFoo":
path/to/ret_type_type_t_subclass.py: error: Incompatible return value type (got "Foo[Bar]", expected "DFoo")  [return-value]
path/to/ret_type_type_t_subclass.py: note: At top level:
path/to/ret_type_type_t_subclass.py: error: "Foo[Bar]" has no attribute "dfoo_adds_method"  [attr-defined]

版本

Python==3.8.5
mypy==0.782

要解决此问题,只需将 get_self 函数键入 def get_self(self: S) -> S,其中 S 是某种类型的变量。

下面的程序将干净利落地进行类型检查:

from __future__ import annotations

from typing import Generic, TypeVar, cast

T = TypeVar("T")

# This can also be just 'S = TypeVar("S")', but that would mean
# we won't be able to use any methods of Foo inside of get_self.
S = TypeVar("S", bound="Foo")


class Foo(Generic[T]):
    def get_self(self: S) -> S:
        return self

class Bar:
    pass


class DFoo(Foo[Bar]):
    def do_something_get_self(self) -> DFoo:
        return self.get_self()

    def dfoo_adds_method(self) -> None:
        pass


dfoo = DFoo()
dfoo.do_something_get_self().dfoo_adds_method()

之所以可行,是因为总是可以覆盖 self 的默认类型。虽然它通常会自动给出当前 class 的类型,但 PEP 484 实际上并不强制您坚持使用此默认值。

因此,我们将其改为泛型,以确保输出类型始终匹配当前子类型。

有关此交互的更多详细信息,请参阅 https://mypy.readthedocs.io/en/stable/generics.html#generic-methods-and-generic-self