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

使用类装饰器装饰__init__时触发MyPy 'Unsupported decorated constructor type'错误的问题咨询

使用类装饰器装饰__init__时触发MyPy 'Unsupported decorated constructor type'错误的问题咨询

我最近尝试用类形式的装饰器来装饰另一个类的方法,但当把这个装饰器应用到__init__构造函数上时,MyPy抛出了Unsupported decorated constructor type的错误,同时在实例化类时还出现了Returning Any from function declared to return "Foo"的问题。我想搞清楚这是我的代码逻辑有问题,还是MyPy本身的限制/ bug?

以下是我的代码:

from types import MethodType
from typing import TypeVar, Callable, Type, ParamSpec, Generic

T = TypeVar("T")
P = ParamSpec("P")
R = TypeVar("R")

class method_decorator(Generic[P, R]):
    def __init__(self, method: Callable[P, R]):
        self.method = method

    def __get__(self, instance: T, cls: Type[T]) -> Callable[..., R]:
        return MethodType(self, instance)

    def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
        return self.method(*args, **kwargs)

class Foo:
    @method_decorator # error: Unsupported decorated constructor type  [misc]
    def __init__(self, x: int):
        self.x = x

    @method_decorator
    def mul(self, a: int) -> int:
        return self.x * a

def gen_foo(x: int) -> Foo:
    return Foo(x) # error: Returning Any from function declared to return "Foo"  [no-any-return]

foo = gen_foo(2)
print(foo.mul(3))

用MyPy v1.13.0启用--strict模式检查的结果:

$ mypy --strict test.py
test.py:19: error: Unsupported decorated constructor type  [misc]
test.py:28: error: Returning Any from function declared to return "Foo"  [no-any-return]
Found 2 errors in 1 file (checked 1 source file)

问题分析

这其实不是你的代码运行时的错误,而是MyPy对构造函数__init__的装饰有特殊限制:

MyPy目前不支持用实现了描述符协议(即包含__get__方法)的类装饰器来装饰__init__。因为构造函数的调用逻辑和普通实例方法不同,MyPy无法正确推断被描述符装饰后的构造函数类型,进而导致后续实例化Foo时被推断为Any,触发第二个返回类型错误。

解决方案

你可以根据需求选择以下几种方案:

方案1:改用函数式装饰器装饰__init__

函数式装饰器是MyPy完全支持的构造函数装饰方式,你可以为__init__单独使用函数式装饰器,普通方法依然保留类装饰器:

from types import MethodType
from typing import TypeVar, Callable, Type, ParamSpec, Generic

T = TypeVar("T")
P = ParamSpec("P")
R = TypeVar("R")

class method_decorator(Generic[P, R]):
    def __init__(self, method: Callable[P, R]):
        self.method = method

    def __get__(self, instance: T, cls: Type[T]) -> Callable[..., R]:
        return MethodType(self, instance)

    def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
        return self.method(*args, **kwargs)

# 新增函数式装饰器
def func_decorator(func: Callable[P, R]) -> Callable[P, R]:
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
        return func(*args, **kwargs)
    return wrapper

class Foo:
    @func_decorator
    def __init__(self, x: int):
        self.x = x

    @method_decorator
    def mul(self, a: int) -> int:
        return self.x * a

def gen_foo(x: int) -> Foo:
    return Foo(x) # 现在MyPy可以正确推断类型

foo = gen_foo(2)
print(foo.mul(3))

方案2:忽略MyPy的构造函数装饰错误

如果你想继续使用类装饰器,可以通过# type: ignore[misc]抑制__init__的装饰错误,MyPy就能正确推断后续的实例化类型:

class Foo:
    @method_decorator  # type: ignore[misc]
    def __init__(self, x: int):
        self.x = x

    @method_decorator
    def mul(self, a: int) -> int:
        return self.x * a

def gen_foo(x: int) -> Foo:
    return Foo(x) # 错误消失

方案3:调整类装饰器的类型注解

你可以修改类装饰器的__get__方法返回类型,让MyPy能更好地识别构造函数的类型(需要添加一个类型忽略来绕过MethodType的类型不匹配):

class method_decorator(Generic[P, R]):
    def __init__(self, method: Callable[P, R]):
        self.method = method

    def __get__(self, instance: T, cls: Type[T]) -> Callable[P, R]:
        return MethodType(self, instance)  # type: ignore[return-value]

    def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
        return self.method(*args, **kwargs)

这种方式可以让MyPy接受对__init__的装饰,同时保留类装饰器的功能。

备注:内容来源于stack exchange,提问作者s417-lama

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 14:42:59