Python 对象创建机制深度解析__new__和__init__的区别以及什么时候该重写__new__很多 Python 开发者第一次接触面向对象编程时都会记住一句话__init__是构造函数。这句话不能说完全错但并不准确。严格来说__init__并不负责“创建对象”它只负责“初始化对象”。真正创建对象的是另一个更底层、更少被日常业务代码直接接触的方法__new__。如果你曾经看到过这样的代码classUser:def__new__(cls,*args,**kwargs):print(__new__ called)returnsuper().__new__(cls)def__init__(self,name):print(__init__ called)self.namename userUser(Alice)输出结果是__new__ called __init__ called那么问题来了为什么创建一个对象要分两步__new__和__init__到底谁先执行什么时候我们需要重写__new__如果乱用它会带来什么问题这篇文章会从基础语法讲到高级实战帮助你真正理解 Python 对象创建机制。无论你是刚开始学习 Python编程 的初学者还是正在做框架、ORM、缓存系统、元编程设计的资深开发者都能从中找到实用价值。一、先给结论__new__创建对象__init__初始化对象最核心的区别可以用一句话概括__new__负责创建并返回一个对象实例__init__负责给这个已经创建好的对象设置初始状态。也就是说当我们写objMyClass()Python 内部大致会经历这样的过程调用 MyClass.__new__(MyClass, ...) ↓ 创建并返回实例对象 instance ↓ 如果 instance 是 MyClass 或其子类的实例 ↓ 调用 MyClass.__init__(instance, ...) ↓ 返回初始化后的对象可以用一个简单示意图表示MyClass(...) | v __new__(cls, ...) | | 创建对象 v instance | v __init__(self, ...) | | 初始化属性 v 最终对象请注意__new__的第一个参数通常叫cls表示当前类__init__的第一个参数通常叫self表示已经创建出来的实例对象。二、用代码观察执行顺序我们先写一个最简单的类classPerson:def__new__(cls,*args,**kwargs):print(1. __new__开始创建对象)instancesuper().__new__(cls)print(2. __new__对象已创建,instance)returninstancedef__init__(self,name):print(3. __init__开始初始化对象)self.namenameprint(4. __init__初始化完成)pPerson(Alice)print(5. 最终对象,p.name)输出类似1.__new__开始创建对象2.__new__对象已创建__main__.Personobjectat 0x...3.__init__开始初始化对象4.__init__初始化完成5.最终对象 Alice从这里可以清楚看到__new__先执行负责“生出对象”。__init__后执行负责“给对象填资料”。这就像开一家新公司__new__负责注册公司主体拿到营业执照__init__负责装修办公室、招聘员工、配置系统。没有公司主体就谈不上初始化但光有公司主体没有初始化也无法真正运转。三、__init__不是构造函数它不能返回对象很多初学者会误以为__init__可以返回某个值比如classUser:def__init__(self,name):self.namenamereturnself这会直接报错TypeError:__init__()shouldreturnNone,notUser原因很简单对象已经由__new__创建并返回了__init__的职责只是初始化这个对象它必须返回None。正确写法是classUser:def__init__(self,name):self.namename如果你确实需要控制“返回哪个对象”那就不是__init__的工作而是__new__的工作。四、__new__可以决定返回什么对象来看一个很有意思的例子classA:def__new__(cls):print(A.__new__ called)returnI am not an A instancedef__init__(self):print(A.__init__ called)objA()print(obj)print(type(obj))输出A.__new__ called I amnotan A instanceclassstr你会发现A.__init__没有被调用。为什么因为__new__返回的是一个字符串对象而不是A的实例。Python 发现返回值不是当前类或其子类的实例就不会继续调用__init__。这说明__new__拥有比__init__更底层的控制权。它不仅能创建对象还能决定是否创建、创建哪个类的对象、是否复用已有对象。这也是为什么我们平时很少重写__new__它太强大也更容易误伤代码的可读性和稳定性。五、标准写法如何正确重写__new__如果确实需要重写__new__通常应该这样写classMyClass:def__new__(cls,*args,**kwargs):instancesuper().__new__(cls)returninstancedef__init__(self,value):self.valuevalue注意几个关键点第一__new__是一个静态风格的方法虽然你不需要显式写staticmethod但它接收的是类对象cls不是实例对象self。第二通常要调用super().__new__(cls)这一步才是真正向父类申请创建实例。第三__new__必须返回一个对象。如果忘了返回classBad:def__new__(cls):print(create)那么objBad()print(obj)结果是None而且__init__也不会正常执行。六、什么时候需要重写__new__日常业务开发中90% 以上的场景只需要写__init__不需要碰__new__。例如classProduct:def__init__(self,name,price):self.namename self.priceprice这是最常见、最清晰、最推荐的方式。但在一些特殊场景中__new__非常有用。七、场景一实现单例模式单例模式要求一个类在程序运行期间只创建一个实例。比如配置中心、日志管理器、数据库连接管理器等。classSettings:_instanceNonedef__new__(cls,*args,**kwargs):ifcls._instanceisNone:print(创建新的 Settings 实例)cls._instancesuper().__new__(cls)else:print(复用已有 Settings 实例)returncls._instancedef__init__(self,env):self.envenv s1Settings(dev)s2Settings(prod)print(s1iss2)print(s1.env)print(s2.env)输出创建新的 Settings 实例 复用已有 Settings 实例Trueprod prod这里虽然实例只创建了一次但__init__被调用了两次所以env最后变成了prod。这是单例模式中非常常见的坑。可以加一个初始化标记classSettings:_instanceNonedef__new__(cls,*args,**kwargs):ifcls._instanceisNone:cls._instancesuper().__new__(cls)cls._instance._initializedFalsereturncls._instancedef__init__(self,env):ifself._initialized:returnself.envenv self._initializedTrues1Settings(dev)s2Settings(prod)print(s1iss2)print(s1.env)输出Truedev这就是一个更安全的单例实现。不过在实际项目中我建议谨慎使用单例。单例会引入全局状态可能让测试、并发和依赖管理变得复杂。如果只是共享配置有时使用模块级变量、依赖注入或配置对象会更清晰。八、场景二继承不可变类型时修改创建逻辑这是__new__最经典、最合理的使用场景之一。比如我们想创建一个始终大写的字符串类型classUpperStr(str):def__new__(cls,value):valuevalue.upper()returnsuper().__new__(cls,value)sUpperStr(hello python)print(s)print(type(s))输出HELLO PYTHONclass__main__.UpperStr为什么这里不能用__init__因为str是不可变对象。字符串对象一旦创建内容就不能再被修改。等__init__执行时字符串内容已经固定了。错误示例classBadUpperStr(str):def__init__(self,value):valuevalue.upper()这不会改变字符串对象本身。再看一个数字范围限制的例子classPercent(float):def__new__(cls,value):ifnot0value1:raiseValueError(Percent 必须在 0 到 1 之间)returnsuper().__new__(cls,value)pPercent(0.85)print(p)如果传入非法值pPercent(1.5)会抛出ValueError:Percent 必须在0到1之间对于int、float、str、tuple这类不可变类型如果你要在创建阶段改变值、校验值、规范化值通常就需要重写__new__。九、场景三对象缓存与享元模式有些对象创建成本较高或者同一个值对应的对象应该被复用这时可以在__new__中做缓存。例如创建一个颜色对象classColor:_cache{}def__new__(cls,name):normalizedname.lower()ifnormalizedincls._cache:print(复用缓存对象)returncls._cache[normalized]print(创建新对象)instancesuper().__new__(cls)cls._cache[normalized]instancereturninstancedef__init__(self,name):self.namename.lower()red1Color(Red)red2Color(red)print(red1isred2)输出创建新对象 复用缓存对象True这就是享元模式的基本思想相同状态的对象共享同一份实例减少内存占用。但这里仍有一个问题__init__可能会重复执行。如果初始化逻辑复杂最好也加初始化保护classColor:_cache{}def__new__(cls,name):normalizedname.lower()ifnormalizedincls._cache:returncls._cache[normalized]instancesuper().__new__(cls)cls._cache[normalized]instance instance._initializedFalsereturninstancedef__init__(self,name):ifself._initialized:returnself.namename.lower()self._initializedTrue这种模式适合值对象、枚举式对象、配置对象等场景。但不适合有大量可变状态的对象否则共享实例可能导致状态串扰。十、场景四控制实例创建返回子类对象有时我们希望根据参数决定创建不同类型的对象。这种思想常见于工厂模式。例如classShape:def__new__(cls,kind,*args,**kwargs):ifclsisShape:ifkindcircle:returnsuper().__new__(Circle)elifkindrectangle:returnsuper().__new__(Rectangle)else:raiseValueError(未知图形类型)returnsuper().__new__(cls)classCircle(Shape):def__init__(self,kind,radius):self.radiusradiusclassRectangle(Shape):def__init__(self,kind,width,height):self.widthwidth self.heightheight shape1Shape(circle,10)shape2Shape(rectangle,3,4)print(type(shape1))print(type(shape2))输出class__main__.Circleclass__main__.Rectangle不过这种写法虽然展示了__new__的能力但实际项目中我更推荐显式工厂函数defcreate_shape(kind,*args):ifkindcircle:returnCircle(*args)ifkindrectangle:returnRectangle(*args)raiseValueError(未知图形类型)原因是工厂函数更直接、更容易测试也更容易被团队成员理解。__new__适合框架底层或对象协议设计业务代码中如果只是想根据参数创建不同对象工厂函数通常更好。十一、__new__、__init__与元类的关系如果继续往 Python 高级编程深入你会遇到元类 metaclass。普通对象是由类创建的而类本身也是对象它由元类创建。对象 instance 由 class 创建 class 又由 metaclass 创建例如classUser:passuUser()print(type(u))print(type(User))输出class__main__.Userclasstype这里User这个类对象本身是由type创建的。当你写classUser:passPython 背后会创建一个类对象。更底层地看类的创建也会经历类似的__new__和__init__过程只不过这里的方法属于元类。简单示例classLoggingMeta(type):def__new__(mcls,name,bases,namespace):print(创建类,name)returnsuper().__new__(mcls,name,bases,namespace)def__init__(cls,name,bases,namespace):print(初始化类,name)super().__init__(name,bases,namespace)classUser(metaclassLoggingMeta):pass输出创建类 User 初始化类 User这和普通对象的创建过程非常相似普通类.__new__ 创建实例对象 普通类.__init__ 初始化实例对象 元类.__new__ 创建类对象 元类.__init__ 初始化类对象这也是很多 ORM、序列化框架、插件系统、声明式 API 的基础。比如框架可以在类创建时读取字段定义、注册路由、收集模型元数据从而让开发者写出更简洁的代码。但要注意元类和__new__都属于“强力工具”。强力工具的最佳使用方式不是到处使用而是在真正需要控制底层行为时使用。十二、项目实战一个可校验的配置对象假设我们要写一个配置类要求相同环境只创建一个配置实例并且环境名统一小写。需求如下1. Config(DEV) 和 Config(dev) 应该返回同一个对象 2. 环境只允许 dev、test、prod 3. 配置对象创建后保存 env 和 debug 信息实现如下classConfig:_instances{}_allowed_envs{dev,test,prod}def__new__(cls,env):normalized_envenv.lower()ifnormalized_envnotincls._allowed_envs:raiseValueError(f不支持的环境{env})ifnormalized_envincls._instances:returncls._instances[normalized_env]instancesuper().__new__(cls)cls._instances[normalized_env]instance instance._initializedFalsereturninstancedef__init__(self,env):ifself._initialized:returnself.envenv.lower()self.debugself.env!prodself._initializedTruedev1Config(DEV)dev2Config(dev)prodConfig(prod)print(dev1isdev2)print(dev1.env,dev1.debug)print(prod.env,prod.debug)输出TruedevTrueprodFalse这个案例中__new__的职责是规范化参数 校验环境 控制是否复用对象 创建实例__init__的职责是设置实例属性 完成业务初始化 避免重复初始化这就是比较清晰的职责划分。十三、常见错误与排查建议1. 忘记返回实例错误写法classA:def__new__(cls):super().__new__(cls)正确写法classA:def__new__(cls):returnsuper().__new__(cls)2. 在__init__中返回值错误写法classA:def__init__(self):returnself__init__必须返回None。3. 单例中重复初始化错误写法classLogger:_instanceNonedef__new__(cls,filename):ifcls._instanceisNone:cls._instancesuper().__new__(cls)returncls._instancedef__init__(self,filename):self.filenamefilename每次调用Logger(...)都会重新设置filename。如果不希望这样就要加初始化标记。4. 对普通业务类过度使用__new__如果只是设置属性请用__init__classOrder:def__init__(self,order_id,amount):self.order_idorder_id self.amountamount没有必要写成classOrder:def__new__(cls,order_id,amount):instancesuper().__new__(cls)instance.order_idorder_id instance.amountamountreturninstance后者不仅啰嗦还破坏了读者对 Python 类初始化流程的正常预期。十四、最佳实践如何选择__new__和__init__可以记住下面这张判断表只是给对象设置属性 使用 __init__ 需要在对象创建前修改不可变对象的值 使用 __new__ 需要控制是否创建新对象 使用 __new__ 需要返回已有缓存对象 使用 __new__ 需要实现单例或享元模式 可以使用 __new__但要谨慎 只是根据参数创建不同类型对象 优先考虑工厂函数 需要控制类对象本身的创建 考虑 metaclass 的 __new__我的经验是业务代码优先保持简单框架代码才需要深入控制。优秀的 Python代码 不一定是最“高级”的代码而是让读者一眼能看懂意图、让维护者半年后还能安心修改的代码。十五、给初学者的理解模型如果你刚学 Python可以把它理解成__new__分配房子 __init__装修房子__new__决定有没有这套房、是哪一套房、是不是复用旧房。__init__决定房子里放什么家具、刷什么颜色、住进谁。再换一个程序员更熟悉的比喻__new__申请内存并生成对象 __init__给对象属性赋值虽然 Python 不要求你像 C/C 那样手动管理内存但理解这个区别会让你在学习 Python高级编程、元编程、框架源码时更从容。十六、延伸和装饰器、上下文管理器、生成器的关系理解__new__和__init__不只是为了回答面试题。它还会帮助你理解 Python 生态中很多高级设计。装饰器可以在函数或类创建后包装行为deftimer(func):importtimedefwrapper(*args,**kwargs):starttime.time()resultfunc(*args,**kwargs)endtime.time()print(f{func.__name__}花费时间{end-start:.4f}秒)returnresultreturnwrapper上下文管理器通过__enter__和__exit__控制资源生命周期classFileManager:def__init__(self,filename):self.filenamefilenamedef__enter__(self):self.fileopen(self.filename,w,encodingutf-8)returnself.filedef__exit__(self,exc_type,exc,tb):self.file.close()生成器通过yield延迟产生数据defread_lines(lines):forlineinlines:yieldline.strip()这些机制看似分散其实都体现了 Python 的一个核心思想对象行为可以通过协议方法精细控制。__new__和__init__正是对象生命周期协议中最基础、也最关键的两环。十七、总结从“会用类”到“理解对象生命周期”回到最初的问题__new__和__init__的区别是什么什么时候需要重写__new__答案可以总结为__new__是对象创建阶段的方法第一个参数是cls必须返回一个对象。它适合用于不可变类型定制、单例模式、对象缓存、享元模式、控制实例返回等底层场景。__init__是对象初始化阶段的方法第一个参数是self不能返回非None的值。它适合绝大多数日常业务开发用来设置实例属性、检查参数、完成初始化逻辑。真正成熟的 Python 开发者不是把每个高级特性都用到项目里而是知道什么时候该简单什么时候该深入。能用__init__解决的问题不要强行使用__new__需要控制对象创建过程时也不要害怕进入底层。Python 的优雅恰恰在于它既能让初学者用几行代码完成任务也能让资深工程师深入对象模型、元编程、异步 I/O、数据科学、人工智能和 Web 框架底层构建高质量的软件系统。如果你正在学习 Python教程建议你把本文的几个例子亲手运行一遍尤其是单例、不可变类型继承和缓存对象这三个案例。真正的理解往往不是来自“看懂了”而是来自“跑过了、改过了、踩过坑又修好了”。最后留两个问题给你你在项目中是否遇到过对象被重复初始化、缓存对象状态串扰、单例难以测试的问题如果让你设计一个数据库连接池你会选择用__new__、工厂函数还是依赖注入来管理对象生命周期欢迎在评论区分享你的实践经验。技术成长从来不是一个人的独行而是一群人把踩过的坑照亮让后来者走得更稳。
Python 对象创建机制深度解析:`__new__` 和 `__init__` 的区别,以及什么时候该重写 `__new__`
发布时间:2026/6/2 14:51:53
Python 对象创建机制深度解析__new__和__init__的区别以及什么时候该重写__new__很多 Python 开发者第一次接触面向对象编程时都会记住一句话__init__是构造函数。这句话不能说完全错但并不准确。严格来说__init__并不负责“创建对象”它只负责“初始化对象”。真正创建对象的是另一个更底层、更少被日常业务代码直接接触的方法__new__。如果你曾经看到过这样的代码classUser:def__new__(cls,*args,**kwargs):print(__new__ called)returnsuper().__new__(cls)def__init__(self,name):print(__init__ called)self.namename userUser(Alice)输出结果是__new__ called __init__ called那么问题来了为什么创建一个对象要分两步__new__和__init__到底谁先执行什么时候我们需要重写__new__如果乱用它会带来什么问题这篇文章会从基础语法讲到高级实战帮助你真正理解 Python 对象创建机制。无论你是刚开始学习 Python编程 的初学者还是正在做框架、ORM、缓存系统、元编程设计的资深开发者都能从中找到实用价值。一、先给结论__new__创建对象__init__初始化对象最核心的区别可以用一句话概括__new__负责创建并返回一个对象实例__init__负责给这个已经创建好的对象设置初始状态。也就是说当我们写objMyClass()Python 内部大致会经历这样的过程调用 MyClass.__new__(MyClass, ...) ↓ 创建并返回实例对象 instance ↓ 如果 instance 是 MyClass 或其子类的实例 ↓ 调用 MyClass.__init__(instance, ...) ↓ 返回初始化后的对象可以用一个简单示意图表示MyClass(...) | v __new__(cls, ...) | | 创建对象 v instance | v __init__(self, ...) | | 初始化属性 v 最终对象请注意__new__的第一个参数通常叫cls表示当前类__init__的第一个参数通常叫self表示已经创建出来的实例对象。二、用代码观察执行顺序我们先写一个最简单的类classPerson:def__new__(cls,*args,**kwargs):print(1. __new__开始创建对象)instancesuper().__new__(cls)print(2. __new__对象已创建,instance)returninstancedef__init__(self,name):print(3. __init__开始初始化对象)self.namenameprint(4. __init__初始化完成)pPerson(Alice)print(5. 最终对象,p.name)输出类似1.__new__开始创建对象2.__new__对象已创建__main__.Personobjectat 0x...3.__init__开始初始化对象4.__init__初始化完成5.最终对象 Alice从这里可以清楚看到__new__先执行负责“生出对象”。__init__后执行负责“给对象填资料”。这就像开一家新公司__new__负责注册公司主体拿到营业执照__init__负责装修办公室、招聘员工、配置系统。没有公司主体就谈不上初始化但光有公司主体没有初始化也无法真正运转。三、__init__不是构造函数它不能返回对象很多初学者会误以为__init__可以返回某个值比如classUser:def__init__(self,name):self.namenamereturnself这会直接报错TypeError:__init__()shouldreturnNone,notUser原因很简单对象已经由__new__创建并返回了__init__的职责只是初始化这个对象它必须返回None。正确写法是classUser:def__init__(self,name):self.namename如果你确实需要控制“返回哪个对象”那就不是__init__的工作而是__new__的工作。四、__new__可以决定返回什么对象来看一个很有意思的例子classA:def__new__(cls):print(A.__new__ called)returnI am not an A instancedef__init__(self):print(A.__init__ called)objA()print(obj)print(type(obj))输出A.__new__ called I amnotan A instanceclassstr你会发现A.__init__没有被调用。为什么因为__new__返回的是一个字符串对象而不是A的实例。Python 发现返回值不是当前类或其子类的实例就不会继续调用__init__。这说明__new__拥有比__init__更底层的控制权。它不仅能创建对象还能决定是否创建、创建哪个类的对象、是否复用已有对象。这也是为什么我们平时很少重写__new__它太强大也更容易误伤代码的可读性和稳定性。五、标准写法如何正确重写__new__如果确实需要重写__new__通常应该这样写classMyClass:def__new__(cls,*args,**kwargs):instancesuper().__new__(cls)returninstancedef__init__(self,value):self.valuevalue注意几个关键点第一__new__是一个静态风格的方法虽然你不需要显式写staticmethod但它接收的是类对象cls不是实例对象self。第二通常要调用super().__new__(cls)这一步才是真正向父类申请创建实例。第三__new__必须返回一个对象。如果忘了返回classBad:def__new__(cls):print(create)那么objBad()print(obj)结果是None而且__init__也不会正常执行。六、什么时候需要重写__new__日常业务开发中90% 以上的场景只需要写__init__不需要碰__new__。例如classProduct:def__init__(self,name,price):self.namename self.priceprice这是最常见、最清晰、最推荐的方式。但在一些特殊场景中__new__非常有用。七、场景一实现单例模式单例模式要求一个类在程序运行期间只创建一个实例。比如配置中心、日志管理器、数据库连接管理器等。classSettings:_instanceNonedef__new__(cls,*args,**kwargs):ifcls._instanceisNone:print(创建新的 Settings 实例)cls._instancesuper().__new__(cls)else:print(复用已有 Settings 实例)returncls._instancedef__init__(self,env):self.envenv s1Settings(dev)s2Settings(prod)print(s1iss2)print(s1.env)print(s2.env)输出创建新的 Settings 实例 复用已有 Settings 实例Trueprod prod这里虽然实例只创建了一次但__init__被调用了两次所以env最后变成了prod。这是单例模式中非常常见的坑。可以加一个初始化标记classSettings:_instanceNonedef__new__(cls,*args,**kwargs):ifcls._instanceisNone:cls._instancesuper().__new__(cls)cls._instance._initializedFalsereturncls._instancedef__init__(self,env):ifself._initialized:returnself.envenv self._initializedTrues1Settings(dev)s2Settings(prod)print(s1iss2)print(s1.env)输出Truedev这就是一个更安全的单例实现。不过在实际项目中我建议谨慎使用单例。单例会引入全局状态可能让测试、并发和依赖管理变得复杂。如果只是共享配置有时使用模块级变量、依赖注入或配置对象会更清晰。八、场景二继承不可变类型时修改创建逻辑这是__new__最经典、最合理的使用场景之一。比如我们想创建一个始终大写的字符串类型classUpperStr(str):def__new__(cls,value):valuevalue.upper()returnsuper().__new__(cls,value)sUpperStr(hello python)print(s)print(type(s))输出HELLO PYTHONclass__main__.UpperStr为什么这里不能用__init__因为str是不可变对象。字符串对象一旦创建内容就不能再被修改。等__init__执行时字符串内容已经固定了。错误示例classBadUpperStr(str):def__init__(self,value):valuevalue.upper()这不会改变字符串对象本身。再看一个数字范围限制的例子classPercent(float):def__new__(cls,value):ifnot0value1:raiseValueError(Percent 必须在 0 到 1 之间)returnsuper().__new__(cls,value)pPercent(0.85)print(p)如果传入非法值pPercent(1.5)会抛出ValueError:Percent 必须在0到1之间对于int、float、str、tuple这类不可变类型如果你要在创建阶段改变值、校验值、规范化值通常就需要重写__new__。九、场景三对象缓存与享元模式有些对象创建成本较高或者同一个值对应的对象应该被复用这时可以在__new__中做缓存。例如创建一个颜色对象classColor:_cache{}def__new__(cls,name):normalizedname.lower()ifnormalizedincls._cache:print(复用缓存对象)returncls._cache[normalized]print(创建新对象)instancesuper().__new__(cls)cls._cache[normalized]instancereturninstancedef__init__(self,name):self.namename.lower()red1Color(Red)red2Color(red)print(red1isred2)输出创建新对象 复用缓存对象True这就是享元模式的基本思想相同状态的对象共享同一份实例减少内存占用。但这里仍有一个问题__init__可能会重复执行。如果初始化逻辑复杂最好也加初始化保护classColor:_cache{}def__new__(cls,name):normalizedname.lower()ifnormalizedincls._cache:returncls._cache[normalized]instancesuper().__new__(cls)cls._cache[normalized]instance instance._initializedFalsereturninstancedef__init__(self,name):ifself._initialized:returnself.namename.lower()self._initializedTrue这种模式适合值对象、枚举式对象、配置对象等场景。但不适合有大量可变状态的对象否则共享实例可能导致状态串扰。十、场景四控制实例创建返回子类对象有时我们希望根据参数决定创建不同类型的对象。这种思想常见于工厂模式。例如classShape:def__new__(cls,kind,*args,**kwargs):ifclsisShape:ifkindcircle:returnsuper().__new__(Circle)elifkindrectangle:returnsuper().__new__(Rectangle)else:raiseValueError(未知图形类型)returnsuper().__new__(cls)classCircle(Shape):def__init__(self,kind,radius):self.radiusradiusclassRectangle(Shape):def__init__(self,kind,width,height):self.widthwidth self.heightheight shape1Shape(circle,10)shape2Shape(rectangle,3,4)print(type(shape1))print(type(shape2))输出class__main__.Circleclass__main__.Rectangle不过这种写法虽然展示了__new__的能力但实际项目中我更推荐显式工厂函数defcreate_shape(kind,*args):ifkindcircle:returnCircle(*args)ifkindrectangle:returnRectangle(*args)raiseValueError(未知图形类型)原因是工厂函数更直接、更容易测试也更容易被团队成员理解。__new__适合框架底层或对象协议设计业务代码中如果只是想根据参数创建不同对象工厂函数通常更好。十一、__new__、__init__与元类的关系如果继续往 Python 高级编程深入你会遇到元类 metaclass。普通对象是由类创建的而类本身也是对象它由元类创建。对象 instance 由 class 创建 class 又由 metaclass 创建例如classUser:passuUser()print(type(u))print(type(User))输出class__main__.Userclasstype这里User这个类对象本身是由type创建的。当你写classUser:passPython 背后会创建一个类对象。更底层地看类的创建也会经历类似的__new__和__init__过程只不过这里的方法属于元类。简单示例classLoggingMeta(type):def__new__(mcls,name,bases,namespace):print(创建类,name)returnsuper().__new__(mcls,name,bases,namespace)def__init__(cls,name,bases,namespace):print(初始化类,name)super().__init__(name,bases,namespace)classUser(metaclassLoggingMeta):pass输出创建类 User 初始化类 User这和普通对象的创建过程非常相似普通类.__new__ 创建实例对象 普通类.__init__ 初始化实例对象 元类.__new__ 创建类对象 元类.__init__ 初始化类对象这也是很多 ORM、序列化框架、插件系统、声明式 API 的基础。比如框架可以在类创建时读取字段定义、注册路由、收集模型元数据从而让开发者写出更简洁的代码。但要注意元类和__new__都属于“强力工具”。强力工具的最佳使用方式不是到处使用而是在真正需要控制底层行为时使用。十二、项目实战一个可校验的配置对象假设我们要写一个配置类要求相同环境只创建一个配置实例并且环境名统一小写。需求如下1. Config(DEV) 和 Config(dev) 应该返回同一个对象 2. 环境只允许 dev、test、prod 3. 配置对象创建后保存 env 和 debug 信息实现如下classConfig:_instances{}_allowed_envs{dev,test,prod}def__new__(cls,env):normalized_envenv.lower()ifnormalized_envnotincls._allowed_envs:raiseValueError(f不支持的环境{env})ifnormalized_envincls._instances:returncls._instances[normalized_env]instancesuper().__new__(cls)cls._instances[normalized_env]instance instance._initializedFalsereturninstancedef__init__(self,env):ifself._initialized:returnself.envenv.lower()self.debugself.env!prodself._initializedTruedev1Config(DEV)dev2Config(dev)prodConfig(prod)print(dev1isdev2)print(dev1.env,dev1.debug)print(prod.env,prod.debug)输出TruedevTrueprodFalse这个案例中__new__的职责是规范化参数 校验环境 控制是否复用对象 创建实例__init__的职责是设置实例属性 完成业务初始化 避免重复初始化这就是比较清晰的职责划分。十三、常见错误与排查建议1. 忘记返回实例错误写法classA:def__new__(cls):super().__new__(cls)正确写法classA:def__new__(cls):returnsuper().__new__(cls)2. 在__init__中返回值错误写法classA:def__init__(self):returnself__init__必须返回None。3. 单例中重复初始化错误写法classLogger:_instanceNonedef__new__(cls,filename):ifcls._instanceisNone:cls._instancesuper().__new__(cls)returncls._instancedef__init__(self,filename):self.filenamefilename每次调用Logger(...)都会重新设置filename。如果不希望这样就要加初始化标记。4. 对普通业务类过度使用__new__如果只是设置属性请用__init__classOrder:def__init__(self,order_id,amount):self.order_idorder_id self.amountamount没有必要写成classOrder:def__new__(cls,order_id,amount):instancesuper().__new__(cls)instance.order_idorder_id instance.amountamountreturninstance后者不仅啰嗦还破坏了读者对 Python 类初始化流程的正常预期。十四、最佳实践如何选择__new__和__init__可以记住下面这张判断表只是给对象设置属性 使用 __init__ 需要在对象创建前修改不可变对象的值 使用 __new__ 需要控制是否创建新对象 使用 __new__ 需要返回已有缓存对象 使用 __new__ 需要实现单例或享元模式 可以使用 __new__但要谨慎 只是根据参数创建不同类型对象 优先考虑工厂函数 需要控制类对象本身的创建 考虑 metaclass 的 __new__我的经验是业务代码优先保持简单框架代码才需要深入控制。优秀的 Python代码 不一定是最“高级”的代码而是让读者一眼能看懂意图、让维护者半年后还能安心修改的代码。十五、给初学者的理解模型如果你刚学 Python可以把它理解成__new__分配房子 __init__装修房子__new__决定有没有这套房、是哪一套房、是不是复用旧房。__init__决定房子里放什么家具、刷什么颜色、住进谁。再换一个程序员更熟悉的比喻__new__申请内存并生成对象 __init__给对象属性赋值虽然 Python 不要求你像 C/C 那样手动管理内存但理解这个区别会让你在学习 Python高级编程、元编程、框架源码时更从容。十六、延伸和装饰器、上下文管理器、生成器的关系理解__new__和__init__不只是为了回答面试题。它还会帮助你理解 Python 生态中很多高级设计。装饰器可以在函数或类创建后包装行为deftimer(func):importtimedefwrapper(*args,**kwargs):starttime.time()resultfunc(*args,**kwargs)endtime.time()print(f{func.__name__}花费时间{end-start:.4f}秒)returnresultreturnwrapper上下文管理器通过__enter__和__exit__控制资源生命周期classFileManager:def__init__(self,filename):self.filenamefilenamedef__enter__(self):self.fileopen(self.filename,w,encodingutf-8)returnself.filedef__exit__(self,exc_type,exc,tb):self.file.close()生成器通过yield延迟产生数据defread_lines(lines):forlineinlines:yieldline.strip()这些机制看似分散其实都体现了 Python 的一个核心思想对象行为可以通过协议方法精细控制。__new__和__init__正是对象生命周期协议中最基础、也最关键的两环。十七、总结从“会用类”到“理解对象生命周期”回到最初的问题__new__和__init__的区别是什么什么时候需要重写__new__答案可以总结为__new__是对象创建阶段的方法第一个参数是cls必须返回一个对象。它适合用于不可变类型定制、单例模式、对象缓存、享元模式、控制实例返回等底层场景。__init__是对象初始化阶段的方法第一个参数是self不能返回非None的值。它适合绝大多数日常业务开发用来设置实例属性、检查参数、完成初始化逻辑。真正成熟的 Python 开发者不是把每个高级特性都用到项目里而是知道什么时候该简单什么时候该深入。能用__init__解决的问题不要强行使用__new__需要控制对象创建过程时也不要害怕进入底层。Python 的优雅恰恰在于它既能让初学者用几行代码完成任务也能让资深工程师深入对象模型、元编程、异步 I/O、数据科学、人工智能和 Web 框架底层构建高质量的软件系统。如果你正在学习 Python教程建议你把本文的几个例子亲手运行一遍尤其是单例、不可变类型继承和缓存对象这三个案例。真正的理解往往不是来自“看懂了”而是来自“跑过了、改过了、踩过坑又修好了”。最后留两个问题给你你在项目中是否遇到过对象被重复初始化、缓存对象状态串扰、单例难以测试的问题如果让你设计一个数据库连接池你会选择用__new__、工厂函数还是依赖注入来管理对象生命周期欢迎在评论区分享你的实践经验。技术成长从来不是一个人的独行而是一群人把踩过的坑照亮让后来者走得更稳。