如何为函数标注两个绑定同一父类的泛型参数?
我想要编写一个接收两个参数的函数,这两个参数需兼容同一基类:
def example_function(desired_type: type[DesiredType], base_object: RelatedType) -> DesiredType: ...
其中RelatedType和DesiredType应为绑定到同一父类的类型变量(该父类仅在调用函数传入具体参数时确定)。
这类函数的一个典型示例是工厂类方法,它可基于另一个被认为在特定上下文下部分“兼容”的对象的属性,创建同类实例:
class Example: @classmethod def from_existing(cls: type[SelfType], existing_object: OtherType) -> SelfType: ... SelfType = TypeVar("SelfType", bound=Example) OtherType = TypeVar("OtherType", bound=Example)
但OtherType在泛型函数签名中仅出现一次,因此会被标记为reportInvalidTypeVarUse错误。此示例中,简单的解决方法是将OtherType替换为Example本身,它自然兼容其子类。
我了解无法强制不同类型变量之间的继承关系,例如:
BaseType = TypeVar("BaseType") DerivedType = TypeVar("DerivedType", bound=BaseType)
因为泛型类型不能作为上界。请问如何提供有意义的类型提示,以传达这种预期的关系?
方法一:单类型变量约束公共基类
直接定义一个绑定到公共基类的类型变量,同时约束两个参数的类型范围,既满足同一基类的关联要求,又避免类型变量单出现的错误:
from typing import TypeVar, Type Base = TypeVar("Base") def example_function(desired_type: type[Base], base_object: Base) -> Base: # 示例实现:复制属性创建新实例 return desired_type(**{k: v for k, v in base_object.__dict__.items() if not k.startswith("_")})
调用时,类型检查器会自动推导Base为传入的desired_type和base_object的共同基类,确保二者类型兼容。
方法二:用Protocol定义兼容接口
如果base_object不需要严格属于同一继承体系,只需要具备特定属性或方法,用Protocol定义兼容接口是更灵活的选择:
from typing import TypeVar, Type, Protocol class HasCoreAttrs(Protocol): id: str name: str T = TypeVar("T", bound=HasCoreAttrs) def example_function(desired_type: type[T], base_object: HasCoreAttrs) -> T: # 基于协议属性创建实例 return desired_type(id=base_object.id, name=base_object.name)
只要base_object实现了HasCoreAttrs协议的属性,无论它属于哪个类体系,都能被函数接受,同时desired_type必须是该协议的子类。
方法三:工厂方法的简化写法(Python 3.11+)
针对你提到的工厂类场景,Python 3.11引入的Self类型可以自动推导子类类型,结合基类约束参数,写法更简洁:
from typing import Self class Example: def __init__(self, value: int): self.value = value @classmethod def from_existing(cls, existing_object: "Example") -> Self: return cls(existing_object.value)
Self会自动对应调用from_existing的子类类型,existing_object用基类Example约束,天然兼容所有子类实例,无需额外定义类型变量。
内容的提问来源于stack exchange,提问作者Vexx23

