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

实现带类型提示的自定义DeepCopy时,调用未类型化类__new__方法触发Mypy报错的问题咨询

Mypy报错:调用未类型化类的__new__时不匹配重载变体

问题回顾

你碰到的情况确实挺让人困惑的:给自定义deepcopy函数加类型提示时,当TypeVar绑定未类型化的tf.Module,Mypy会对cls.__new__(cls)抛出重载不匹配错误,但换成已类型化的类型(比如int、自定义类)或者把逻辑改成类方法实现时,检查就能正常通过。先把核心场景再理清楚:

最小复现代码:

T = TypeVar("T", bound=Any)
def foo(x: T) -> None:
    cls = type(x)
    cls.__new__(cls)

Mypy报错:

error: No overload variant of "__new__" of "type" matches argument type "Type[Any]"
note: Possible overload variants:
note: def __new__(cls, cls: Type[type], o: object) -> type
note: def __new__(cls, cls: Type[type], name: str, bases: Tuple[type, ...], namespace: Dict[str, Any]) -> type

你的实际业务代码(绑定tf.Module)和类方法实现的对比也很关键——类方法里Mypy完全不报错,这背后其实是Mypy对未类型化类的类型推导逻辑在起作用。

原因拆解

这个问题的核心在于Mypy对未类型化类的处理规则:

  1. 像tf.Module这种没有提供类型注解的类,Mypy会把它视为Any的子类。当你用type(x)获取它的类型时,Mypy只能推导出Type[Any]。
  2. Mypy内置的type.__new__重载主要针对动态创建新类的场景(比如type("NewClass", (), {})这种用法),而不是调用已有类的__new__来创建实例。所以当它看到Type[Any]调用__new__时,只会去匹配那些创建类的重载,自然就不匹配了。
  3. 当你绑定已类型化的类时,type(x)会被推导为Type[T],Mypy能识别到这是一个可以创建实例的类类型,会匹配实例创建的__new__重载,所以没问题。
  4. 类方法中self: T的类型注解让Mypy明确知道type(self)是Type[T],而且在类的上下文中,Mypy会优先默认类存在创建实例的__new__签名(哪怕类未类型化),所以不会报错。

解决方案

这里有几个实用的解决办法,你可以根据场景选择:

1. 显式注解cls的类型为Type[T]

直接给cls加上类型注解,告诉Mypy它是T的类型,而不是泛化的Type[Any]:

import tensorflow as tf
from typing import TypeVar, Type, Dict
import copy

T = TypeVar("T", bound=tf.Module)

def my_copy(x: T, memo: Dict[int, object]) -> T:
    do_something_with_a_tf_module(x)
    cls: Type[T] = type(x)  # 显式标注cls的类型
    new = cls.__new__(cls)
    memo[id(x)] = new  # 修正原代码里的self为x
    for name, value in x.__dict__.items():
        setattr(new, name, copy.deepcopy(value, memo))
    return new

这样Mypy就能正确识别到你是在调用类的__new__创建实例,匹配正确的重载。

2. 使用cast强制修正类型

如果显式注解觉得麻烦,可以用cast来告诉Mypycls的实际类型:

from typing import cast, TypeVar, Type, Dict
import tensorflow as tf
import copy

T = TypeVar("T", bound=tf.Module)

def my_copy(x: T, memo: Dict[int, object]) -> T:
    do_something_with_a_tf_module(x)
    cls = cast(Type[T], type(x))
    new = cls.__new__(cls)
    memo[id(x)] = new
    for name, value in x.__dict__.items():
        setattr(new, name, copy.deepcopy(value, memo))
    return new

3. 安装TensorFlow的类型存根

如果你的项目经常用到TensorFlow,最彻底的办法是安装官方的类型存根,让Mypy能识别tf.Module的正确类型:

pip install types-tensorflow

安装后,TypeVar("T", bound=tf.Module)会被Mypy正确处理,type(x)会自动推导为Type[T],报错会自动消失,不需要额外的注解或cast。

补充说明

关于Mypy的__new__重载问题:确实,Mypy内置的type.__new__重载看起来和Python官方文档里的__new__不太一致——官方文档里__new__主要用来创建实例,但Mypy的重载更偏向动态类创建的场景。这其实不是Bug,而是Mypy对未类型化类的处理限制:当类没有类型注解时,Mypy无法知道它的__new__签名,只能 fallback 到type的通用重载。

内容的提问来源于stack exchange,提问作者joel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 06:58:13