问题描述
我有一个Generic
基类,它通过一种方法(get_self
)返回自身。我已经按类型提示了它。
然后,我有了该基类的子类,该子类为Generic
传递了arg类型。在那个孩子班上,我叫get_self
。我想将类型提示更新为只是子类的名称。
但是,mypy==0.782
正在报告error: Incompatible return value type (got "Foo[Bar]",expected "DFoo") [return-value]
。有什么方法可以做到这一点?
**编辑**
我决定在进一步思考后重新解释这个问题。抱歉,冗长。
- 基类(
Foo
)的方法(get_self
)类型提示返回其自身的实例 - 子类(
DFoo
)不会覆盖该方法 - 子类然后使用(
get_self
)方法- 并且知道返回类型实际上将是子类(
DFoo
)
- 并且知道返回类型实际上将是子类(
- 但是,静态类型检查器(例如:
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。