
本文介绍解决 Pyre 报错 Invalid class instantiation [45] 的核心方法:用 Protocol 替代 type[ABC] 作为工厂返回类型,既保留构造函数签名提示,又避免 Pyre 将抽象类误判为待实例化对象。
本文介绍解决 pyre 报错 `invalid class instantiation [45]` 的核心方法:用 `protocol` 替代 `type[abc]` 作为工厂返回类型,既保留构造函数签名提示,又避免 pyre 将抽象类误判为待实例化对象。
在使用抽象基类(ABC)配合工厂模式进行类型提示时,Pyre 的行为与其他主流类型检查器(如 mypy、pyright)存在关键差异。其根本原因在于:Pyre 将任何带有 @abstractmethod 的类(即使未继承 ABC)视为不可实例化的抽象类,并严格禁止对 type[MyClassABC] 类型值执行调用操作——哪怕该值实际指向的是具体子类。
例如,以下代码在 Pyre 中会触发错误:
from abc import abstractmethod
class MyClassABC:
@abstractmethod
def __init__(self, name: str) -> None:
pass
class MyClass(MyClassABC):
def __init__(self, name: str) -> None:
self.name = name
# ❌ Pyre 报错:Invalid class instantiation [45]
cls: type[MyClassABC] = MyClass
instance = cls(name="test") # ← 此处被误判为尝试实例化抽象类
虽然 cls 实际绑定的是具体类 MyClass,但 Pyre 基于类型注解 type[MyClassABC] 推断出“可能为抽象类”,从而拒绝调用。这与 mypy(仅在赋值时警告)和 pyright(完全允许)的策略不同。
✅ 正确解法:用 Protocol 表达可调用性
要兼顾 IDE 参数提示、运行时正确性与 Pyre 兼容性,应放弃 type[MyClassABC],改用结构化协议(Protocol)显式描述构造函数签名:
from abc import abstractmethod
from typing import Protocol, Dict, Type, Any
class MyClassABC:
@abstractmethod
def __init__(self, name: str) -> None:
pass
# ✅ 定义一个 Protocol,精确描述“可被 name: str 调用并返回 MyClassABC 实例”的行为
class MyClassFactory(Protocol):
def __call__(self, name: str) -> MyClassABC: ...
class MyClass(MyClassABC):
def __init__(self, name: str) -> None:
self.name = name
class MyClass2(MyClassABC):
def __init__(self, name: str) -> None:
self.name = name
class ClassFactory:
# 类型改为 Union[MyClassFactory, Type[MyClassABC]] —— 兼容具体类与协议
MAPPING: Dict[str, MyClassFactory | Type[MyClassABC]] = {
"myclass": MyClass,
"myclass2": MyClass2,
}
@staticmethod
def get_class() -> MyClassFactory:
return ClassFactory.MAPPING["myclass"]
# ✅ Pyre、mypy、pyright 全部通过,且 IDE 可识别 name 参数提示
if __name__ == "__main__":
factory_callable = ClassFactory.get_class()
instance = factory_callable(name="test") # ✔️ 安全调用,类型推导为 MyClassABC
print(instance.name) # ✔️ IDE 可识别属性(需配合 runtime 类型)
? 关键要点说明
-
Protocol 是结构化而非名义化:
MyClassFactory不要求类显式继承,只要具备匹配的__call__签名即满足协议,天然兼容具体类(如MyClass)。 -
签名必须严格一致:
__call__参数与返回类型需与目标 ABC 的__init__完全对应(包括参数名、类型、顺序及返回MyClassABC),否则类型检查将失败。 -
类型联合提升鲁棒性:
MyClassFactory | Type[MyClassABC]允许映射中同时存入协议实例或具体类,兼顾灵活性与静态检查精度。 - 无需修改运行时逻辑:所有改动仅影响类型注解,零 runtime 开销,100% 向后兼容。
⚠️ 注意事项
- 避免在
MyClassABC中定义非__init__的@abstractmethod并期望 Protocol 自动继承——Protocol 必须显式声明所有需约束的方法。 - 若构造函数签名复杂(如含
*args,**kwargs, 多重重载),建议使用@overload配合 Protocol,或考虑typing.Callable(但会丢失参数名提示)。 - Pyre 对
Protocol的支持自 v1.0+ 已稳定,确保使用较新版本(≥1.12.0)以获得最佳体验。
通过此方案,你既能享受 ABC 带来的接口契约保障与 IDE 智能提示,又能彻底规避 Pyre 的误报,实现真正健壮、跨检查器兼容的工厂模式类型设计。










