mypy使用子项的名称而不是父项方法的通用签名更新子项方法的返回值

问题描述

我有一个Generic基类,它通过一种方法(get_self)返回自身。我已经按类型提示了它。

然后,我有了该基类的子类,该子类为Generic传递了arg类型。在那个孩子班上,我叫get_self。我想将类型提示更新为只是子类的名称。

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


**编辑**

我决定在进一步思考后重新解释这个问题。抱歉,冗长。

  1. 基类(Foo)的方法(get_self)类型提示返回其自身的实例
  2. 子类(DFoo)不会覆盖该方法
  3. 子类然后使用(get_self)方法
    • 并且知道返回类型实际上将是子类(DFoo
  4. 但是,静态类型检查器(例如:mypy)不知道子类的方法实际上将返回子类的对象,因为它们正在使用基类的类型提示

因此,如果不使用新的类型提示在子类中重新声明方法(get_self),我的问题就不可能实现。

我可以将get_self的返回值设为TypeVar。但是,由于基类Foo已经是Generic,因此目前尚不可能,因为它需要python/typing Higher-Kinded TypeVars #548中提到的“高级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是某种类型var。

然后,以下程序将干净地键入check:

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。尽管通常会自动为您提供当前类的类型,但PEP 484实际上并没有要求您坚持使用该默认值。

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

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

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...