简体   繁体   English

Python 根据属性类型定义返回类型

[英]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.

相关问题 python 如何使用预定义类型为 function(参数和返回类型)定义类型? - python How to define a type for a function (arguments and return type) with a predefined type? Python 类型提示:根据属性返回类型 - Python type hinting: return type based on attribute 在 PySpark 中定义一个 UDF,其中返回类型基于列 - Define an UDF in PySpark where the return type is based on a column 定义子类的返回值类型,而无需在 python 中重新定义父实现 - Define return values type of subclasses without redefining parent implementation in python 在python中,如何根据返回类型重载返回/获取? - In python, how to overload return/get based on return type? 如何在Python中为类型提示定义类型的别名 - How to define an alias for a type in Python for type hinting 如何在python中定义匿名类型 - How to define an anonymous type in python 返回 self 的 python 通用属性用法的返回值类型不兼容 - Incompatible return value type of python generic property usage that returns self Python 打字,mypy 根据 class 方法返回类型推断返回类型 - Python typing, mypy infer return type based on class method return type 基于 Python 中的 class 变量的 class 方法中的返回类型 - Return type in a class method based on a class variable in Python
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM