如何为给定类型的严格子类添加Python类型提示?
如何为数据类字段添加“严格子类/子类实例”的类型提示?
我需要给数据类的字段添加类型提示,要求字段值是某个类的子类实例或者子类类型,但不能是该类本身。举个具体示例:
class Foo: ... class Bar(Foo): ... class Baz(Foo): ... @dataclass class Data: foo_subclass_instance: StrictSubclassOf[Foo] # 仅接受Bar或Baz的实例,不接受Foo foo_subclass: type[StrictSubclassOf[Foo]] # 仅接受Bar或Baz类型,不接受Foo
如果子类数量少且提前已知,可以用这种变通方案:
@dataclass class Data: foo_subclass_instance: Bar | Baz foo_subclass: type[Bar] | type[Baz]
但子类数量繁多,或是部分子类因导入循环无法直接导入时,这个方案就不再适用。那么是否存在类似StrictSubclassOf[T]的注解可以实现需求?
Python标准类型提示库中没有内置StrictSubclassOf[T]这类直接注解,但可以通过以下几种方式实现类似效果:
1. 用Protocol实现静态类型检查(针对实例)
定义一个仅由Foo子类自动实现的协议,Foo本身不满足该协议,以此让类型检查器区分严格子类实例:
from dataclasses import dataclass from typing import Protocol, TypeVar T = TypeVar('T', covariant=True) class StrictSubclassProtocol(Protocol[T]): """标记为父类T的严格子类实例的协议""" __orig_bases__: tuple[type[T], ...] class Foo: ... class Bar(Foo): ... class Baz(Foo): ... @dataclass class Data: foo_subclass_instance: StrictSubclassProtocol[Foo]
注:这种方式依赖mypy等类型检查器对Protocol的支持,且__orig_bases__是CPython的实现细节,跨解释器可能存在兼容性问题。
2. 自定义类型+运行时验证
如果需要同时支持静态提示和运行时校验,可以自定义类型,并配合数据类的字段验证器:
from dataclasses import dataclass, field from typing import TypeVar, Type, Any T = TypeVar('T') class StrictSubclassOf: def __class_getitem__(cls, item: Type[T]) -> Type[T]: return type(f"StrictSubclassOf[{item.__name__}]", (), {}) def validate_strict_subclass_instance(value: Any, parent: Type[T]) -> None: if isinstance(value, parent) and type(value) is parent: raise ValueError(f"值不能是{parent.__name__}本身,必须是其子类实例") def validate_strict_subclass_type(value: Any, parent: Type[T]) -> None: if issubclass(value, parent) and value is parent: raise ValueError(f"值不能是{parent.__name__}本身,必须是其子类类型") @dataclass class Data: foo_subclass_instance: StrictSubclassOf[Foo] = field( validator=lambda self, val: validate_strict_subclass_instance(val, Foo) ) foo_subclass: type[StrictSubclassOf[Foo]] = field( validator=lambda self, val: validate_strict_subclass_type(val, Foo) )
这种方式在运行时会严格拦截不符合要求的类型,同时静态检查器会将StrictSubclassOf[Foo]识别为Foo的子类实例类型。
3. 简化静态提示写法(依赖文档约定)
如果只需要静态类型提示,可结合TypeVar的bound参数,再通过文档明确排除父类本身:
from typing import TypeVar, Type from dataclasses import dataclass FooSubclassInstance = TypeVar('FooSubclassInstance', bound=Foo) FooSubclassType = TypeVar('FooSubclassType', bound=Type[Foo]) @dataclass class Data: # 仅接受Foo的严格子类实例,不包含Foo本身 foo_subclass_instance: FooSubclassInstance # 仅接受Foo的严格子类类型,不包含Foo本身 foo_subclass: FooSubclassType
这种写法最简洁,但需要开发者遵守文档约定,静态检查器不会自动排除Foo本身。
内容的提问来源于stack exchange,提问作者Beefster
相关产品推荐
相关产品推荐

