如何正确调用Python中定义的泛型property函数?
泛型描述符工厂函数的类型标注问题
问题重现
你定义了一个持有EventBus状态的泛型描述符工厂函数,但使用时出现类型检查错误:
原始定义代码
from typing import TypeVar, Generic, Callable, Any class EventBus: def subscribe(self): print("Subscribed") def publish(self): print("Published") T = TypeVar('T') def create_property(event_bus: EventBus): 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() def property(validator: Callable[[T], bool] = lambda x: True) -> Property[T]: return Property(validator) return property
使用时的错误
property: Callable[..., Property[T]] = create_property(EventBus()) class MyComponent: width = property() # 类型提示:Type of "width" is "Property[Unknown]" height: int = property(lambda x: x > 0) # 错误:"Property[Unknown]"与声明类型"int"不兼容
错误原因
- 泛型参数作用域错误:外部定义的
T无法被create_property内部返回的property函数正确绑定,导致类型检查器无法推导泛型参数,出现Property[Unknown]。 - 属性类型标注错误:
height: int的标注逻辑错误——property(...)返回的是Property[int]类型的描述符实例,而非int类型的属性值。描述符的作用是管理实例上的int属性,但描述符本身的类型不是int。
解决方案
1. 调整泛型参数作用域
将TypeVar定义在create_property函数内部,确保泛型参数绑定到返回的property函数上,让类型检查器能够正确推导。
2. 修正类型标注逻辑
类中的描述符属性应标注为Property[T](对应泛型类型),实例上的属性类型会通过描述符的__get__方法自动推导。如果需要明确指定泛型类型,可以显式传递泛型参数。
修正后的完整代码
from typing import TypeVar, Generic, Callable, Any, Type class EventBus: def subscribe(self): print("Subscribed") def publish(self): print("Published") def create_property(event_bus: EventBus): # 将TypeVar定义在函数内部,绑定到当前返回的property函数 T = TypeVar('T') class Property(Generic[T]): def __init__(self, validator: Callable[[T], bool]): self._validator = validator self.name: str = "" def __set_name__(self, obj: Any, name: str): self.name = name def __get__(self, obj: Any, obj_type: Type[Any] | None = None) -> T: # 假设实例属性已初始化,若需支持未初始化可返回Optional[T] return obj.__dict__[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() # 明确标注泛型函数的类型 def property(validator: Callable[[T], bool] = lambda x: True) -> Property[T]: return Property(validator) return property
正确使用方式
# 初始化工厂函数 my_property = create_property(EventBus()) class MyComponent: # 方式1:显式指定泛型参数 width: Property[int] = my_property[int]() # 方式2:通过validator的类型推导泛型(需给validator加类型提示) height: Property[int] = my_property(lambda x: isinstance(x, int) and x > 0) # 实例化后,类型检查器会自动识别实例属性的类型为int comp = MyComponent() comp.width = 10 # 类型检查通过 comp.height = 5 # 类型检查通过,且会触发validator校验
关键要点
- 泛型参数必须定义在工厂函数内部,确保作用域正确绑定,避免类型推导失败。
- 类中的描述符属性类型是
Property[T],实例属性的类型才是T,类型检查器会通过描述符的__get__方法返回值自动识别。 - Python 3.9+支持泛型函数的显式参数指定(如
my_property[int]()),可以更清晰地指定泛型类型。
内容的提问来源于stack exchange,提问作者Adam A
相关产品推荐
相关产品推荐

