如何在Python中创建保留泛型类型信息的工厂函数?
如何在Python中创建保留泛型类型信息的工厂函数?
嘿,我太懂你遇到的这个糟心事了!你想通过工厂函数把EventBus绑定到Property类里,省得每次实例化都要传,但现在类型检查器把返回的类全推断成Property[Any],完全丢了泛型T的类型提示,对吧?
其实这个问题的核心是:你在函数内部动态创建的泛型类,到了函数外面,类型检查器没办法追踪到它原本的泛型参数T,所以只能默认给它标上Any。咱们可以通过两个简单的调整来解决这个问题:
方法一:给工厂函数加明确的泛型返回类型注解+类型转换
直接给create_property函数的返回值加上泛型类型提示,再用cast给类型检查器递个“悄悄话”,告诉它这个类的真实泛型身份,代码调整后是这样的:
from typing import TypeVar, Generic, Callable, Any, Type, cast T = TypeVar('T') def create_property(event_bus: EventBus) -> Type[Generic[T]]: class Property(Generic[T]): def __init__(self, validator: Callable[[T], bool]): self._validator = validator def __set_name__(self, obj: Any, name: str): self.name = name def __get__(self, obj: Any, type: Any) -> T: return obj.__dict__.get(self.name) def __set__(self, obj: Any, value: T): if not self._validator(value): raise ValueError("Invalid value") obj.__dict__[self.name] = value event_bus.publish() # 告诉类型检查器:这个Property是带泛型T的,不是Any! return cast(Type[Generic[T]], Property)
方法二:用预定义泛型基类继承
如果你觉得cast有点“取巧”,也可以先定义一个泛型基类,把通用逻辑都放进去,让动态创建的Property继承它,这样类型检查器就能顺着基类轻松追踪到泛型参数:
from typing import TypeVar, Generic, Callable, Any, Type T = TypeVar('T') # 先搞一个泛型基类,把通用的属性逻辑都封装在这里 class PropertyBase(Generic[T]): def __init__(self, validator: Callable[[T], bool]): self._validator = validator def __set_name__(self, obj: Any, name: str): self.name = name def __get__(self, obj: Any, type: Any) -> T: return obj.__dict__.get(self.name) def __set__(self, obj: Any, value: T): if not self._validator(value): raise ValueError("Invalid value") obj.__dict__[self.name] = value def create_property(event_bus: EventBus) -> Type[PropertyBase[T]]: # 只在子类里加和EventBus绑定的逻辑 class Property(PropertyBase[T]): def __set__(self, obj: Any, value: T): super().__set__(obj, value) event_bus.publish() return Property
测试一下效果
调整完之后,你再用这个工厂函数创建Property类,类型检查器就能正确识别泛型参数了:
class EventBus: def publish(self): print("Event published!") # 创建绑定了EventBus的Property类 MyProperty = create_property(EventBus()) class MyClass: # 这里会正确推断为MyProperty[int],再也不是Any了! number = MyProperty[int](lambda x: isinstance(x, int)) # 要是你给number传个字符串,类型检查器会直接跳出来报错提示
这样不管是赋值还是取值,类型提示都能正常工作,而且EventBus也已经稳稳绑定到Property类里,不用每次实例化都传,完美解决你的需求~
备注:内容来源于stack exchange,提问作者Adam A
相关产品推荐
相关产品推荐

