如何在抽象类/接口中为描述符实现无hack的类型提示?
问题描述
希望创建包含自定义描述符类定义属性的类,同时有一个隐藏实现细节的接口。现有代码能正常运行且类型检查不报错,但将Ten类型强制转换为int的写法不够优雅;若要让描述符支持泛型,中间函数ten还会增加复杂度。
原代码:
from typing import cast class Ten: def __get__(self, obj, objtype=None) -> int: return 10 def __set__(self, obj, value: int) -> None: pass # just to make Ten "writeable" def ten() -> int: return cast(int, Ten()) # <-- this is ugly class ABase: x: int class A(ABase): x: int = ten() def fn(value: ABase) -> int: return value.x a = A() print(fn(a))
请问是否可以在不使用此类魔术hack的前提下,实现一定程度的类型检查且不触发类型检查器报错?
解决方案
完全可以,Python主流类型检查器(如mypy、pyright)原生支持描述符的类型推断,无需强制转换或中间函数。以下是两种优化方案:
方案1:非泛型描述符直接使用
直接移除冗余的ten()函数和cast操作,类型检查器会根据描述符的__get__方法返回值,自动推断实例属性的类型:
class Ten: def __get__(self, obj, objtype=None) -> int: return 10 def __set__(self, obj, value: int) -> None: pass # 保持描述符可写 class ABase: x: int # 接口定义属性类型 class A(ABase): x = Ten() # 类型检查器会识别:实例访问a.x时返回int,符合ABase的接口 def fn(value: ABase) -> int: return value.x a = A() print(fn(a)) # 输出10,类型检查无报错
方案2:支持泛型的描述符
如果需要让描述符支持泛型,可通过继承Generic和TypeVar实现,同时保持类型检查的准确性:
from typing import Generic, TypeVar T = TypeVar('T') # 定义泛型描述符基类 class ValueDescriptor(Generic[T]): def __get__(self, obj, objtype=None) -> T: raise NotImplementedError("需实现__get__方法") def __set__(self, obj, value: T) -> None: raise NotImplementedError("需实现__set__方法") # 基于泛基类实现返回int的Ten描述符 class Ten(ValueDescriptor[int]): def __get__(self, obj, objtype=None) -> int: return 10 def __set__(self, obj, value: int) -> None: pass class ABase: x: int class A(ABase): x = Ten() # 类型检查器自动推断实例属性x为int,符合接口定义 def fn(value: ABase) -> int: return value.x a = A() print(fn(a)) # 输出10,类型检查无报错
原理说明
类型检查器会识别描述符的特殊方法:当类属性是描述符实例时,会以__get__方法的返回类型作为实例访问该属性时的类型,因此无需将描述符实例强制转换为目标类型,就能让子类符合接口的类型要求。
内容的提问来源于stack exchange,提问作者j123b567
相关产品推荐
相关产品推荐

