[英]Python define return type based on property type
I have three classes, one base class and two child classes with the following structure:我有三个类,一个基类和两个具有以下结构的子类:
class BaseLayout:
def add_element(self: "BaseLayout", element: str) -> None:
...
class LayoutA(BaseLayout):
def add_element(self: "BaseLayout", element: str, name: str) -> None:
...
class LayoutB(BaseLayout):
def add_element(self: "BaseLayout", element: str, number: int) -> None:
...
The base class is not directly used but does implement some other functions which are not relevant to the question.基类没有直接使用,但确实实现了一些与问题无关的其他功能。 I then use the layout as property in the Container class:然后,我将布局用作 Container 类中的属性:
class Container:
def __init__(self: "Container", layout: BaseLayout) -> None:
self._current_layout = layout
@property
def layout(self: "Container") -> BaseLayout:
return self._current_layout
Here is my current problem:这是我目前的问题:
If I provided an instance of LayoutA
to the Container
class and then call the add_element
method I get a type checker error Expected 1 positional argument
.如果我向Container
类提供LayoutA
的一个实例,然后调用add_element
方法,我会得到类型检查器错误Expected 1 positional argument
。 Here is the code to add the element:下面是添加元素的代码:
c = Container(LayoutA())
# Expected 1 positional argument
c.add_element("foo", "bar")
How can I provided the correct type hints to the Container class to make this work?我怎样才能向 Container 类提供正确的类型提示以使其工作?
As explained already by @chepner in the comments, your code is first of all not type safe because your BaseLayout
subclasses' add_element
overrides are not compatible with the base signature.正如@chepner 在评论中已经解释的那样,您的代码首先不是类型安全的,因为您的BaseLayout
子类的add_element
覆盖与基本签名不兼容。
You could fix that for example by creating different methods, if you need different behavior, and have them call the superclass' method.例如,如果您需要不同的行为,您可以通过创建不同的方法来解决这个问题,并让它们调用超类的方法。 I don't know the actual use case, so this is just an example.我不知道实际的用例,所以这只是一个例子。 There are many ways to deal with this.有很多方法可以解决这个问题。
class BaseLayout:
def add_element(self, element: str) -> None:
print(f"{element=}")
class LayoutA(BaseLayout):
def add_name_element(self, element: str, name: str) -> None:
super().add_element(element)
print(f"{name=}")
class LayoutB(BaseLayout):
def add_number_element(self, element: str, number: int) -> None:
super().add_element(element)
print(f"{number=}")
Once that is fixed, you can make Container
generic in terms of the layout it uses like this:修复后,您可以根据 Container 使用的布局使Container
通用,如下所示:
from typing import Generic, TypeVar
L = TypeVar("L", bound=BaseLayout)
class Container(Generic[L]):
_current_layout: L # this is optional, but helpful IMO
def __init__(self, layout: L) -> None:
self._current_layout = layout
@property
def layout(self) -> L:
return self._current_layout
ca = Container(LayoutA())
ca.layout.add_name_element("foo", "bar")
cb = Container(LayoutB())
cb.layout.add_number_element("foo", 1)
This is fine and static type checkers should correctly infer the layout
type.这很好,静态类型检查器应该正确推断layout
类型。 If you add reveal_type(ca._current_layout)
and reveal_type(cb._current_layout)
below and run mypy
, you'll get this:如果你在下面添加reveal_type(ca._current_layout)
和reveal_type(cb._current_layout)
并运行mypy
,你会得到这个:
note: Revealed type is "LayoutA"
note: Revealed type is "LayoutB"
This also means if you eg try and call cb.layout.add_name_element
somewhere, the type checker will complain.这也意味着,如果您尝试在某处调用cb.layout.add_name_element
,类型检查器会报错。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.