page contents

Python 中 __new__ 和 __init__ 有什么区别?什么时候会用到 __new__?

搞Python久了,总会遇到一些神神叨叨的魔法方法,什么__str__、__repr__、__call__……这些还算常见,真要说起来最让人挠头的还得是__new__和__init__这俩兄弟。面试官老喜欢问:“说说__new__和__init__的区别?”——这问题不难,但说清楚的人不多。今天我就跟你们唠唠这个事儿,争取让你听完少走点弯路。

attachments-2025-07-oVP1DHGp68672c729cf57.jpg搞Python久了,总会遇到一些神神叨叨的魔法方法,什么__str__、__repr__、__call__……这些还算常见,真要说起来最让人挠头的还得是__new__和__init__这俩兄弟。面试官老喜欢问:“说说__new__和__init__的区别?”——这问题不难,但说清楚的人不多。今天我就跟你们唠唠这个事儿,争取让你听完少走点弯路。

先说个基本的认知误区,很多人一上来就以为对象创建就是__init__的事儿,其实啊,大错特错。__init__只是“初始化”,不是“创建”。对象是在__new__里被“造”出来的,__init__只是负责拿到这个对象之后再给它“装修装修”。所以从流程上看,__new__先执行,然后返回一个实例,接着__init__再对这个实例进行初始化配置。

你可能要问,那我平时写类压根儿没见过__new__,是不是这玩意没啥用?别急,其实这是Python的设计哲学之一:简单场景尽量让你感知不到复杂机制。你写类的时候,除非你明确继承了object,Python内部默认你用的是object.__new__这个标准构造器,它默默帮你把对象创建好了。也就是说,平时我们压根儿没感受到__new__的存在,但它一直在那儿悄悄工作。

可一旦你需要自定义元类、实现单例模式、或者你继承的是不可变类型(比如tuple、str),__new__这老哥就必须亲自出马了。为啥?因为像str、tuple这些东西是immutable的,一旦创建就改不了了,你只能在创建那一刻就决定它的所有属性。而创建这个动作就是在__new__里完成的,错过就没机会了。

举个例子,想实现一个简单的单例模式,我们得重写__new__。为什么?因为你得在创建对象之前判断这个类是不是已经有实例了,有的话就直接返回旧的,不用再造新对象了。像下面这样:

class Singleton:

    _instance = None

    

    def __new__(cls, *args, **kwargs):

        if cls._instance is None:

            cls._instance = super().__new__(cls)

        return cls._instance


    def __init__(self, name):

        self.name = name

你可能发现了,__new__里我们控制了整个“造对象”的过程,而__init__只是设个属性。这段代码跑两次,__new__只创建了一个实例,__init__却可能被调用多次,这就暴露了一个潜在问题:__init__不具备控制实例个数的能力,只有__new__能办到。

再说个例子,继承不可变类型的场景。比如我想继承str类型,加点额外的功能,就必须用__new__,因为str一旦创建就改不了了:

class MyStr(str):

    def __new__(cls, content):

        print("Creating instance")

        return super().__new__(cls, content)

    

    def __init__(self, content):

        print("Initializing instance")

你看,__new__负责生成这个MyStr对象并设置内容,而__init__只是跑一下初始化逻辑,这在str里基本啥也干不了。真要改点值,那得在__new__里处理。

说到这我顺便提个踩过的坑。我之前做一个配置解析器,打算把配置文件读成一个不可变对象,结果用了namedtuple套一层__init__去改默认值,死活不生效。最后才发现人家namedtuple底层就是用__new__处理的,__init__根本没机会动那些字段,白折腾了两小时。

哦对,还有一个经常被忽视的点:__new__的返回值非常重要,它可以返回当前类的实例,也可以返回其他类的实例。对,你没听错,甚至可以返回一个完全不同类的对象!Python对这种“离经叛道”的操作是支持的,但得慎用。比如你想实现某种“对象替身”的机制,可以在__new__里偷偷返回别的对象。

比如这个骚操作:

class B:

    def __init__(self):

        self.value = "I am B"


class A:

    def __new__(cls):

        print("Redirecting instance")

        return B()


a = A()

print(a.value)  # 输出:I am B

你以为你拿到的是A的实例,其实是B的。这种操作说实话挺邪门的,但也确实在某些框架里会看到,比如Django ORM或某些反序列化工具,用来实现“透明代理”。

聊到这,你可能心里还有点没谱:我到底啥时候用__new__?一句话总结:你不需要创建自定义不可变类型,不涉及元类,不搞单例,不做对象替换,就别碰__new__。99%的时候你不需要它,那1%用的时候一定是因为你要控制实例创建过程。

我个人觉得吧,这种设计其实也挺Pythonic的——你不用管它,它就像个安静的工人默默帮你造对象。真到了你非得亲自动手的时候,它也不会躲着你,一切都留有后门。

当然,如果你是那种天天写业务代码的Pythoner,遇到__new__的概率确实不大。但如果你想深入理解Python的对象模型,或者你正在写一些底层库、需要搞自定义类行为,那早点搞明白__new__能帮你省不少事。

最后提一句,这俩方法跟Java里的构造器其实并不对等,Java的构造器相当于Python的__init__,它是对象创建之后的初始化。而Python的对象生命周期是“先__new__再__init__”,从语言设计角度来说更灵活,但也更容易被滥用。

总之,别神化__new__,它就是一个你平时不怎么用、但关键时刻能救你一命的“隐士高手”。真碰到需要它的场景,就别犹豫,上手干就是了。

你要问我:这玩意真的面试常考吗?讲真,一般中高级Python岗位的面试官还真爱拿这个当开胃菜。会了加分,不会不扣分,但你一讲出来,立马给人留下“这个人懂底层”的印象,何乐而不为?

更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

  • 发表于 2025-07-04 09:20
  • 阅读 ( 42 )
  • 分类:Python开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
小柒
小柒

2172 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 2172 文章
  3. Pack 1335 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章