为何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:
- 元类M创建抽象类A时,将
A.__new__(此时是绑定到A的object.__new__类方法)保存到A.__new_orig__; - 替换
A.__new__为抛错的自定义函数; - 处理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()仅接受一个参数”的错误。- 元类M创建抽象类A时,将
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

