如何在函数需访问公共成员时正确声明/使用TypeVar?
类型注解问题:工厂函数访问类公共成员的类型推断疑惑
我尝试给一个工厂函数添加类型注解,该函数返回传入类的实例,同时需要访问类的公共成员。示例代码如下:
import re from dataclasses import dataclass from typing import ClassVar, Type, TypeVar @dataclass class SomeDataClass: core_item: str PATTERN: ClassVar[str] = r"^([a-z]{3})$" def __init__(self, args: tuple[str, ...]) -> None: self.core_item = args[0] @dataclass class AnotherDataClass: core_item: int PATTERN: ClassVar[str] = r"^([0-9]{3})$" def __init__(self, args: tuple[str, ...]) -> None: self.core_item = int(args[0]) Parsable = TypeVar("Parsable", bound=SomeDataClass|AnotherDataClass) def some_factory(result_type: Type[Parsable], line: str) -> Parsable: if not (match := re.match(result_type.PATTERN, line)): raise RuntimeError(f"Could not parse {line} with pattern {result_type.PATTERN}") return result_type(match.groups()) a = some_factory(SomeDataClass, "abc") b = some_factory(AnotherDataClass, "123")
但MyPy抛出了以下错误:
worksnt.py:25: error: "Type[Parsable]" has no attribute "PATTERN" [attr-defined] worksnt.py:26: error: "Type[Parsable]" has no attribute "PATTERN" [attr-defined] worksnt.py:27: error: Incompatible return value type (got "Union[SomeDataClass, AnotherDataClass]", expected "Parsable") [return-value]
我通过定义一个基于Protocol的基类,显式声明公共成员PATTERN和__init__方法,将Parsable绑定到该基类解决了问题,代码如下:
import re from dataclasses import dataclass from typing import ClassVar, Type, TypeVar, Protocol class BaseClass(Protocol): # pylint: disable=too-few-public-methods """Why do I need this?""" PATTERN: ClassVar[str] def __init__(self, args: tuple[str, ...]) -> None: """Also needless""" @dataclass class SomeDataClass: core_item: str PATTERN: ClassVar[str] = r"^([a-z]{3})$" def __init__(self, args: tuple[str, ...]) -> None: self.core_item = args[0] @dataclass class AnotherDataClass: core_item: int PATTERN: ClassVar[str] = r"^([0-9]{3})$" def __init__(self, args: tuple[str, ...]) -> None: self.core_item = int(args[0]) Parsable = TypeVar("Parsable", bound=BaseClass) def some_factory(result_type: Type[Parsable], line: str) -> Parsable: if not (match := re.match(result_type.PATTERN, line)): raise RuntimeError(f"Could not parse {line} with pattern {result_type.PATTERN}") return result_type(match.groups()) a = some_factory(SomeDataClass, "abc") b = some_factory(AnotherDataClass, "123")
但我有个疑惑:为什么必须用基类显式声明PATTERN?MyPy难道不能从包含该成员的类联合中自动推断出来吗?
更新:经同事提醒,将bound设为联合类型存在问题。使用Parsable = TypeVar("Parsable", SomeDataClass, AnotherDataClass)也能正常工作,不过用Protocol可能是更简洁的方案,我还没完全搞懂其中的原理。
内容的提问来源于stack exchange,提问作者frans
相关产品推荐
相关产品推荐

