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

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>

我的疑问

  1. 为何第一次抛出的TypeError提及Empty类,第二次却只提及object.__new__?
  2. 为何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__的核心逻辑可以拆解为以下几步:

  1. 类型校验:首先检查第一个参数是否是合法的Python类型(类),如果不是直接抛出异常。
  2. 实例创建:为该类型分配内存空间,初始化实例的基本结构(如类型指针、引用计数等),返回未初始化的实例。
  3. 参数适配检查:
    • 如果目标类没有重写__new__和__init__(如Empty):会校验传入的参数是否符合object()的签名(无额外参数),不符合则抛出指向目标类的异常。
    • 如果目标类重写了__new__(如Person):直接以原生object.__new__的签名校验,只允许传入类型参数。
    • 如果目标类仅重写了__init__(如Vehicle):会忽略多余参数,因为这些参数是留给__init__使用的,__new__本身不需要处理它们。

简单来说,object.__new__的行为并非固定不变,而是会根据目标类的构造方法重写情况动态调整参数校验逻辑,这是CPython为了适配不同类的构造模式做的底层优化。

内容的提问来源于stack exchange,提问作者Cornélio Sousa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 19:29:58