在开发项目时,我偶然发现了返回自定义属性对象时键入注释的问题。
这是代表问题的代码片段:
from typing import TypeVar, Any
_B = TypeVar("_B")
class CustomProperty(property):
def a_function(): ...
def custom_property(type: _B) -> _B:
return CustomProperty(type)
custom_property(int).a_function() # As passed int, typing shows it as an int when I expect CustomProperty
class ClassWithCustomProperty:
property_holder = custom_property(int) # This shows as an int, correct
在上面的代码中,custom_property 返回 CustomProperty 的实例,custom_property 可以在类内部(充当属性)和模块级别(充当 CustomProperty 实例)使用
在此项目中,使用 custom_property/CustomProperty 作为装饰器不是有效选项。
我尝试过使用
Union[_B, CustomProperty]
但得到 type[int] | CustomProperty
这并不理想,因为我期望在模块级别调用时 CustomProperty
以及在类内调用时 type[int]
提前感谢您,如果有任何不清楚或需要更多背景信息,请随时添加评论。
如果我理解正确的话,这将是期望的结果:
module_level = custom_property(int)
reveal_type(module_level) # CustomProperty[int]
reveal_type(module_level.a_function()) # int
class ClassWithCustomProperty:
property_holder = custom_property(int)
reveal_type(property_holder) # CustomProperty[int]
reveal_type(ClassWithCustomProperty.property_holder) # CustomProperty[int]
reveal_type(ClassWithCustomProperty().property_holder) # int
为此,请使
CustomProperty
通用并重载 .__get__()
:
class CustomProperty[T](property):
@overload
def __get__(self, instance: None, owner: type[Any] | None, /) -> Self: ...
@overload
def __get__(self, instance: Any, owner: type[Any] | None, /) -> T: ...
def __get__(self, instance: Any, owner: type[Any] | None = None) -> T | Self:
return super().__get__(instance, owner)
def a_function(self) -> T: ...
def custom_property[T](type: type[T]) -> CustomProperty[T]:
return CustomProperty(type)
当从类 (instance
) 访问描述符(即
None
)或根本不存在这样的类/实例 (
CustomProperty
) 时,
C.property_holder
是 module_level
,在这种情况下我们返回描述符对象(即 CustomProperty[T]
/Self
)。
否则,描述符是从实例访问的(
C().property_holder
),因此我们返回包含的值(即T
)。