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

为何object.__new__在自定义元类场景下参数行为异常?

问题解答

核心原因:绑定类方法的错误赋值

你的问题根源在于元类处理抽象类时,错误地将绑定到父类A的类方法赋值给了子类B的__new__属性,导致方法调用时的参数传递异常。


1. 为何Y的__new__可接受参数而B不行?

  • 对于普通子类Y:
    Y.__new__是Python通过MRO正常查找得到的object.__new__类方法描述符,访问时会自动绑定到Y类,形成绑定方法。当你调用Y(3)时,默认的type.__call__会执行Y.__new__(Y,3),等价于object.__new__(Y,3),参数传递完全符合object.__new__的签名(cls + 任意给__init__的参数),因此能正常创建实例。

  • 对于子类B:

    1. 元类M创建抽象类A时,将A.__new__(此时是绑定到A的object.__new__类方法)保存到A.__new_orig__;
    2. 替换A.__new__为抛错的自定义函数;
    3. 处理B时,直接将B.__new__赋值为A.__new_orig__——也就是那个已经绑定到A的方法。

    当调用B(5)时,type.__call__尝试执行B.__new__(B,5),但此时B.__new__的第一个参数已经被固定为A(绑定方法的特性),实际调用的是object.__new__(A, B,5),传递了3个参数给object.__new__,超出了其预期的参数数量,因此触发“object.new()仅接受一个参数”的错误。


2. 同一个object.__new__方法为何呈现不同行为与异常处理?

你看到的“同一个object.__new__”是假象:

  • Y的__new__是object.__new__类方法描述符绑定到Y后的绑定方法;
  • B的__new__是object.__new__类方法描述符绑定到A后的绑定方法。

这两个绑定方法底层指向同一个内置函数,但绑定上下文完全不同:

  • 调用Y.__new__(Y,3)等价于object.__new__(Y,3),参数合法,能正常创建实例;
  • 调用B.__new__(B,5)等价于object.__new__(A, B,5),多传了一个额外的类参数,触发参数数量错误。

而直接调用object.__new__(object,1)时,因为cls是object本身,object.__new__不允许给object实例传递额外参数,报错信息为“takes exactly one argument”,这和B的报错场景(传递多余的cls参数)不同,因此异常信息存在差异。


修复方案

修改元类M,保存未绑定的object.__new__方法,而非绑定到A的方法:

class AbstractClassError(TypeError):
    pass

class M(type):
    def __new__(cls, name, bases, attrs):
        new_cls = super().__new__(cls, name, bases, attrs)
        if attrs.get('__abstract__', False):
            # 直接获取object类中未绑定的__new__描述符
            new_cls.__new_orig__ = object.__dict__['__new__']
            # 替换为抛错的方法
            def __new__(cls_, *args, **kwargs):
                raise AbstractClassError(f"无法实例化抽象类 {cls_.__name__}")
            new_cls.__new__ = __new__
        else:
            # 为非抽象子类恢复未绑定的__new__方法
            for base in bases:
                if hasattr(base, '__new_orig__'):
                    new_cls.__new__ = base.__new_orig__
                    break
        return new_cls

这样,B.__new__会被赋值为未绑定的类方法描述符,访问时自动绑定到B类,调用时参数传递正常。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 21:27:02