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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 23:02:34