Python3.12+:classprop装饰器类型检查错误的解决方法咨询
Python 3.11+ 实现通过类型检查的只读类属性的现代方式
问题背景
在探索Python 3.11+下实现只读类属性的方案时,参考了相关讨论中的一个实现思路,改写后在Python 3.12+环境中测试,却触发了类型检查错误。
代码示例
from functools import update_wrapper from typing import Callable class classprop[T]: def __init__(self, method: Callable[..., T]): self.method = method update_wrapper(self, method) def __get__(self, obj, cls=None) -> T: if cls is None: cls = type(obj) return self.method(cls) class MyClass: @classprop def hello(self) -> str: return "world" x = MyClass() x.hello
触发的类型错误
Argument of type "Self@classprop[T@classprop]" cannot be assigned to parameter "wrapper" of type "(**_PWrapper@update_wrapper) -> _RWrapper@update_wrapper" in function "update_wrapper" Type "Self@classprop[T@classprop]" is incompatible with type "(**_PWrapper@update_wrapper) -> _RWrapper@update_wrapper"
问题解答
1. 错误具体含义
functools.update_wrapper的类型定义要求第二个参数(wrapper)必须是可调用对象(符合Callable类型的函数/类实例),但我们传入的是classprop的实例——它是描述符类的实例,未实现__call__方法,不属于标准可调用类型,因此类型检查器判定两者类型不兼容。
2. 错误是否合理
这个错误是合理的。update_wrapper的设计目的是为包装函数/可调用对象保留原对象的元信息(如__name__、__doc__),而classprop实例的核心作用是作为描述符,通过__get__方法提供类属性的访问逻辑,并非作为可调用对象使用。类型检查器严格遵循类型定义,报错符合预期。
3. 解决方法(无需屏蔽错误)
最优雅的方式是手动复制原方法的元信息,替代update_wrapper:
from typing import Callable class classprop[T]: def __init__(self, method: Callable[..., T]): self.method = method # 手动复制元信息,替代update_wrapper self.__name__ = method.__name__ self.__doc__ = method.__doc__ self.__module__ = method.__module__ def __get__(self, obj, cls=None) -> T: if cls is None: cls = type(obj) return self.method(cls) class MyClass: @classprop def hello(self) -> str: return "world" x = MyClass() print(x.hello) # 输出: world
如果需要保留update_wrapper的全部功能(比如复制更多元信息),也可以通过类型转换让检查器通过:
from functools import update_wrapper from typing import Callable, cast class classprop[T]: def __init__(self, method: Callable[..., T]): self.method = method # 用cast强制转换类型,让类型检查器认为self是可调用对象 update_wrapper(cast(Callable, self), method) def __get__(self, obj, cls=None) -> T: if cls is None: cls = type(obj) return self.method(cls)
不过这种方式属于“告知检查器忽略类型差异”,不如手动复制元信息严谨。
内容的提问来源于stack exchange,提问作者bzm3r
相关产品推荐
相关产品推荐

