【问题标题】:Annotating return types for methods returning self in mixins为在 mixins 中返回 self 的方法注释返回类型
【发布时间】:2020-05-26 19:22:28
【问题描述】:

我正在使用构建器模式,其中(大)类上的大多数方法都返回它们的标识 (self),因此被注释为返回它们所属的类的类型:

class TextBuilder:
    parts: List[str]           # omitted
    render: Callable[[], str]  # for brevity

    def text(self, val: str) -> "TextBuilder":
        self.parts.append(val)
        return self

    def bold(self, val: str) -> "TextBuilder":
        self.parts.append(f"<b>{val}</b>")
        return self

    ...

示例用法:

joined_text = TextBuilder().text("a ").bold("bold").text(" text").render()  
# a <b>bold</b> text

现在,随着这个类越来越大,我想将相关方法拆分并分组到 mixins 中:

class BaseBuilder:
    parts: List[str]           # omitted
    render: Callable[[], str]  # for brevity


class TextBuilder(BaseBuilder):
    def text(self, val: str):
        self.parts.append(val)
        return self
    ...


class HtmlBuilder(BaseBuilder):
    def bold(self, val: str):
        self.parts.append(f"<b>{val}</b>")
        return self
    ...


class FinalBuilder(TextBuilder, HtmlBuilder):
    pass

但是,我看不到正确注释 mixin 类的返回类型的方法,结果类 FinalBuilder 总是让 mypy 相信它返回 FinalBuilder 而不是 mixin 类之一。当然,假设我想要实际上注释 self 并返回类型,因为它们可能无法从这些方法内部发生的事情中推断出来。

我尝试将 mixin 类设为通用并将它们显式标记为返回类型 T 绑定到 BaseBuilder,但这并不满足 mypy.有任何想法吗?现在我将跳过所有这些恶作剧并在所有地方省略返回类型,因为在使用 FinalBuilder 时应该正确推断它们,但我仍然很好奇是否有一种通用的方法来解决这个问题。

【问题讨论】:

    标签: python types annotations pycharm mypy


    【解决方案1】:

    如果您希望返回类型始终是 self,只需像这样注释 self 参数:

    from typing import List, Callable, TypeVar
    
    T = TypeVar('T', bound=BaseBuilder)
    
    class BaseBuilder:
        parts: List[str]           # omitted
        render: Callable[[], str]  # for brevity
    
    
    class TextBuilder(BaseBuilder):
        def text(self: T, val: str) -> T:
            self.parts.append(val)
            return self
        ...
    
    
    class HtmlBuilder(BaseBuilder):
        def bold(self: T, val: str) -> T:
            self.parts.append(f"<b>{val}</b>")
            return self
        ...
    
    
    class FinalBuilder(TextBuilder, HtmlBuilder):
        pass
    
    
    # Type checks
    f = FinalBuilder().text("foo").bold("bar")
    
    # Mypy states this is type 'FinalBuilder'
    reveal_type(f)
    

    几点说明:

    1. 如果我们不注释self,mypy 通常会假设它是我们当前包含的任何类的类型。但是,如果你愿意,给它一个自定义类型提示实际上是可以的,只要那个类型提示与类兼容。 (例如,将def foo(self: int) -&gt; None 添加到 HtmlBuilder 是不合法的,因为 int 不是 HtmlBuilder 的超类型。)

      我们通过将self 设为通用来利用这一点,以便我们可以指定更具体的返回类型。

      有关详细信息,请参阅 mypy 文档:https://mypy.readthedocs.io/en/stable/generics.html#generic-methods-and-generic-self

    2. 我将 TypeVar 绑定到 BaseBuilder,以便两个函数都能看到 partsrender 字段。如果您希望 text(...)bold(...) 函数也分别查看 TextBuilder 和 HtmlBuilder 中定义的字段,则需要创建两个绑定到这些更具体的子类的 TypeVar。

    【讨论】:

      猜你喜欢
      • 2021-06-06
      • 1970-01-01
      • 2014-01-26
      • 1970-01-01
      • 2020-11-23
      • 1970-01-01
      • 2012-12-02
      • 1970-01-01
      • 2017-03-15
      相关资源
      最近更新 更多