我正在使用构建器模式,其中(大)类上的大多数方法返回其标识(
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
现在,随着这个类变得越来越大,我想将相关方法拆分并分组到混合中:
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
但是,我没有找到一种方法来正确注释混合类的返回类型,从而生成结果类
FinalBuilder
总是让 mypy 相信它返回
FinalBuilder
而不是 mixin 类之一。当然,假设我想
实际上
注释
self
并返回类型,因为它们可能无法从这些方法内部发生的情况推断出来。
我尝试使 mixin 类通用并标记它们明确返回一个绑定到
T
的类型
BaseBuilder
,但这并不能满足mypy.有任何想法吗?现在我将跳过所有这些恶作剧并省略各处的返回类型,因为在使用
FinalBuilder
时应该正确推断它们,但我仍然很好奇是否有通用的方法来解决这个问题。
你遇到的问题是,在 Python 中,你不能对返回类型使用像“mixin 类本身的类型”这样的东西。 当你在 mixin 中定义一个方法时,你不知道它最终会被合并到哪个类中。
不过,有一些方法可以解决这个问题,让你在一定程度上实现类型安全:
1. 使用
TypeVar
和泛型函数:
from typing import List, Callable, TypeVar, Generic
T = TypeVar('T', bound='BaseBuilder')
class BaseBuilder:
parts: List[str]
render: Callable[[], str]
class TextBuilder(BaseBuilder, Generic[T]):
def text(self: T, val: str) -> T:
self.parts.append(val)
return self
class HtmlBuilder(BaseBuilder, Generic[T]):
def bold(self: T, val: str) -> T:
self.parts.append(f"<b>{val}</b>")
return self
class FinalBuilder(TextBuilder[FinalBuilder], HtmlBuilder[FinalBuilder]):
pass
joined_text = FinalBuilder().text("a ").bold("bold").text(" text").render()
在这个例子中,我们使用了
TypeVar
来定义一个名为
T
的类型变量,它被限制为
BaseBuilder
的子类。然后,我们在 mixin 类中使用
Generic[T]
来表明它们是泛型类,并使用
self: T
来指定方法返回的类型是
T
。 最后,在
FinalBuilder
中,我们明确地将
T
指定为
FinalBuilder
,这样 mypy 就知道
text
和
bold
方法会返回
FinalBuilder
实例。
2. 使用
@overload
装饰器 (Python 3.11+):
从 Python 3.11 开始,你可以使用
@overload
装饰器来为不同的返回类型提供多个类型提示:
from typing import List, Callable, overload
class BaseBuilder:
parts: List[str]
render: Callable[[], str]
class TextBuilder(BaseBuilder):
@overload
def text(self: 'TextBuilder', val: str) -> 'TextBuilder': ...
@overload
def text(self, val: str) -> 'FinalBuilder': ...
def text(self, val: str):
self.parts.append(val)
return self
# ... 为 HtmlBuilder.bold 添加类似的 @overload ...
class FinalBuilder(TextBuilder, HtmlBuilder):
pass
这种方法更明确地说明了方法可以返回的不同类型,但也更加冗长,特别是当你有很多 mixin 和方法时。
选择哪种方法取决于你的个人喜好和 Python 版本。 两种方法都能帮助你在使用 mixin 时提高类型安全性。
标签:python,pycharm,python-typing,mypy From: 62029791