You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确调用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"不兼容

错误原因

  1. 泛型参数作用域错误:外部定义的T无法被create_property内部返回的property函数正确绑定,导致类型检查器无法推导泛型参数,出现Property[Unknown]。
  2. 属性类型标注错误: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 00:27:06