Python typing 如何标注“这个参数是类本身而不是实例”

应使用 typing.Type[ClassName](或 Python 3.9+ 的 type[ClassName])标注参数为类本身而非实例,如工厂函数接收 User 类而非 User() 实例,配合 TypeVar 可实现泛型类型关联,避免误用 User(实例)或 type(过于宽泛)。

typing.Type[ClassName] 标注,表示参数是类(类型对象),不是其实例。

基础写法:Type[T] 表示“类本身”

当你希望函数接收一个类(比如用于工厂、反射或类型注册),而不是该类的实例时,应使用 typing.Type

  • Type[Foo] 表示“Foo 这个类”,即 Foo 类型的类型对象(如 Foo 本身,不是 Foo()
  • 它等价于 type[Foo](Python 3.9+ 推荐写法,更简洁)
  • 注意:Foo(未加括号)是类;Foo() 是实例 —— 类型标注要和实际传入值一致

实际例子

比如一个创建实例的工厂函数:

from typing import Type

class User: def init(self, name: str): self.name = name

def create_instance(cls: Type[User], name: str) -> User: return cls(name) # ✅ 正确:cls 是类,可调用

调用时传类,不是实例

u = create_instance(User, "Alice") # ✅

create_instance(User(), "Alice") # ❌ 类型检查器会报错

泛型与动态类:用 TypeVar 更灵活

若函数支持任意类(且返回对应类型的实例),用 TypeVar 保持类型关联:

from typing import Type, TypeVar

T = TypeVar("T", bound=object)

def build(cls: Type[T], *args, *kwargs) -> T: return cls(args, **kwargs)

类型检查器知道:build(Foo, ...) → Foo 实例;build(Bar, ...) → Bar 实例

常见误区

  • ❌ 写成 cls: User:这表示参数必须是 User 的实例,语义完全相反
  • ❌ 写成 cls: type:太宽泛,丢失具体类型信息,无法做返回值推导
  • ✅ Python 3.9+ 可直接用 type[User] 替代

    Type[User],更直观