You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在函数需访问公共成员时正确声明/使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 11:56:35