Python中object.__new__的底层行为及相关疑问解析
CPython中object.__new__的行为疑惑解析
我在CPython 3.10.13中进行元编程实验时,发现object.__new__存在一些令人困惑的行为,它的表现会根据第一个传入的参数发生变化。以下是我的实验代码及注释:
# 空类继承自object的__new__和__init__ class Empty: pass # 验证继承关系 assert Empty.__new__ is object.__new__, "__new__不相同" assert Empty.__init__ is object.__init__, "__init__不相同" empty_obj = Empty() uinit_empty_obj = object.__new__(Empty) assert type(empty_obj) is type(uinit_empty_obj), "类型不同" try: object.__new__(Empty, 10, 'hi', hello='bye') except TypeError as e: # 异常信息中提到了Empty类 print(repr(e)) # 重写object的__new__和__init__方法 # __new__和__init__签名一致 class Person: def __new__(cls, name, age): """仅重写object.__new__,未做额外操作""" print(f'Inside {cls.__name__}.__new__') return super().__new__(cls) def __init__(self, name, age): print(f'Inside {type(self).__name__}.__init__') self.name = name self.age = age a_person = Person('John Doe', 25) uinit_person = Person.__new__(Person, 'Michael', 40) try: # 按常理object()不接受参数,这里应该报错 another_uinit_person = object.__new__(Person, 'Ryan', 25) except TypeError as e: # 确实抛出TypeError,但异常信息未提及Person类 print('`another_uinit_person` :', repr(e)) # 更奇怪的情况(至少对我来说) # 继承object的__new__,重写__init__ # __new__和__init__签名不匹配,这是Python中常见的基础类写法,运行正常 class Vehicle: def __init__(self, model): self.model = model # 验证__new__的继承关系 assert Vehicle.__new__ is object.__new__, "并非继承自object.__new__" a_vehicle = Vehicle('Honda') # 我原本以为CPython会自动生成匹配__init__的__new__,或者生成一个接受所有参数的__new__ # 下面的try-except显示后者似乎成立,但上面的assert又表明Vehicle.__new__确实是object.__new__ try: # 未抛出任何异常 uinit_vehicle = Vehicle.__new__(Vehicle, 'Honda', 10, ('four-wheels',), hello='bye') except Exception as e: print(repr(e)) else: print('`uinit_vehicle` : 构造成功', uinit_vehicle) # 下面的调用同样正常执行 try: # 未抛出任何异常 another_unit_vehicle = object.__new__(Vehicle, 'Toyota') another_unit_vehicle = object.__new__(Vehicle, 'Toyota', 100, four_wheels=True) except Exception as e: print(repr(e)) else: print('`another_unit_vehicle` : 构造成功:', another_unit_vehicle)
运行代码后得到的输出如下:
TypeError('Empty() takes no arguments') Inside Person.__new__ Inside Person.__init__ Inside Person.__new__ `another_uinit_person` : TypeError('object.__new__() takes exactly one argument (the type to instantiate)') `uinit_vehicle` : 构造成功 <__main__.Vehicle object at 0x00000244D15A7A90> `another_unit_vehicle` : 构造成功: <__main__.Vehicle object at 0x00000244D15A7A30>
我的疑问
- 为何第一次抛出的TypeError提及Empty类,第二次却只提及
object.__new__? - 为何
object.__new__(Person, 'Ryan', 25)会抛出TypeError,而object.__new__(Vehicle, 'Toyota')及带更多参数的调用却能正常执行?
核心问题
object.__new__在底层究竟执行了哪些操作?我推测它会对第一个参数的__new__和/或__init__重写方法进行特殊检查。
问题解答
1. 异常信息差异的原因
CPython在处理object.__new__的参数检查时,会根据目标类是否完全继承了object的构造逻辑来决定异常信息的展示:
- 对于
Empty类:它既没有重写__new__也没有重写__init__,当调用object.__new__(Empty, ...)传入多余参数时,CPython会将其等同于直接调用Empty(...)传参错误,因此异常信息会指向Empty类,提示它不接受参数。 - 对于
Person类:它重写了自己的__new__方法,此时object.__new__会直接以自身的参数规则校验——原生object.__new__只接受一个参数(要实例化的类型),因此异常信息会直接指向object.__new__的参数错误。
2. 参数接受情况的差异
这同样和目标类是否重写了构造方法有关:
- 当目标类是
Person时:你重写了__new__,但直接调用object.__new__(Person, ...)时,执行的是原生object.__new__的逻辑,它严格只接受一个类型参数,多余参数会触发TypeError。 - 当目标类是
Vehicle时:它只重写了__init__,没有重写__new__。CPython对这种情况做了特殊处理:原生object.__new__会忽略多余的参数——因为这些参数本来是要传给__init__的,而__new__本身不需要它们。只要第一个参数是合法的类型,object.__new__就会创建实例,多余参数不会被校验。
核心问题:object.__new__的底层操作
object.__new__的核心逻辑可以拆解为以下几步:
- 类型校验:首先检查第一个参数是否是合法的Python类型(类),如果不是直接抛出异常。
- 实例创建:为该类型分配内存空间,初始化实例的基本结构(如类型指针、引用计数等),返回未初始化的实例。
- 参数适配检查:
- 如果目标类没有重写
__new__和__init__(如Empty):会校验传入的参数是否符合object()的签名(无额外参数),不符合则抛出指向目标类的异常。 - 如果目标类重写了
__new__(如Person):直接以原生object.__new__的签名校验,只允许传入类型参数。 - 如果目标类仅重写了
__init__(如Vehicle):会忽略多余参数,因为这些参数是留给__init__使用的,__new__本身不需要处理它们。
- 如果目标类没有重写
简单来说,object.__new__的行为并非固定不变,而是会根据目标类的构造方法重写情况动态调整参数校验逻辑,这是CPython为了适配不同类的构造模式做的底层优化。
内容的提问来源于stack exchange,提问作者Cornélio Sousa
相关产品推荐
相关产品推荐

