new int[10]和new int[10]()的区别 new和init的区别 python中new方法举例
Python中__new__和__init__是对象创建和初始化的两个关键方法。__new__负责实例的创建,而__init__负责实例的初始化。文章将深入探讨它们的调用顺序、在继承链中的行为,以及__new__的正确使用方式,特别是避免在__new__中手动调用__init__或返回非实例对象所导致的常见陷阱,帮助开发者构建健壮的Python类。__new__与__init__的基本概念
在python中,对象的生命周期通常涉及两个特殊方法:
*`new(cls,参数, kwargs)`:这是一个类方法(即使没有使用@classmethod装饰器,它也隐式地接收类作为第一个参数cls)。它在实例被创建调用之前,负责创建并返回一个类的实例。它是真正创建实例的方法,通常通过调用父类的__new__方法(如su per().__new__(cls)或object.__new__(cls))来完成实例的创建。如果__new__没有返回当前类的实例(或者返回None),那么该类的__init__方法将不会被调用。
*`init(self, args,kwargs)`:这是一个实例方法,它在__new__方法返回实例后立即调用。它负责初始化创建实例的状态,例如设置实例的属性。它不返回任何值(隐式返回None)。
调用顺序总结:当你创建一个类的实例时(例如obj = MyClass()),Python解释器会首先调用MyClass.__new__()来创建实例。如果__new__成功创建并返回了一个MyClass的实例,那么解释器会接着调用该实例的MyClass.__init__( ) 方法来进行初始化。__new__ 的正确使用姿势
__new__ 通常只在需要控制实例创建过程的特殊场景下进行重写,例如:单例模式(Singleton):确保一个类只有一个实例。不可变对象(Immutable)元类(元类):高级用法,用于类的自定义创建。
在覆盖__new__时,务必遵循以下原则:
立即学习“Python免费学习笔记参数(深入)”;第一个必须是cls:即使它是一个实例方法,__new__在被调用时,第一个参数永远是当前类。必须返回一个实例:通常是通过调用父类的__new__方法来获取实例,例如实例= super().__new__(cls, *args, **kwargs)。不要在__new__中手动调用__init__:这是Python解释器的工作。如果在__new__中手动调用__init__,可能会导致重复初始化、初始化未完全创建的对象,或者在__new__返回非实例对象时引发错误。继承链中的行为
当继承存在关系时,__new__和__i nit__的调用行为会比较复杂:__new__的调用:当你实例化一个子类的时候,子类的__new__方法会首先被调用。如果子类没有重写__new__,则向上查找父类的__new__方法。__init__的调用:只有当__new__方法返回当前类或者子类的实例时,该实例的__init__方法才会被调用。
如果__new__返回了其他类型的对象,或者返回了None,那么当前类的__init__将不会被执行。案例分析与常见陷阱
我们通过一个示例来深入理解__new__和__init__的复杂交互,特别是当__new__被错误实现时。
考虑以下有问题的代码:class Demo: def __new__(self): # 错误:__new__应接收cls作为第一个参数self.__init__(self) # 陷阱:在__new__中手动调用__init__ print(quot;Demo's __new__() invokedquot;) # 隐式返回None,因为没有显式return def __init__(self): print(quot;Demo's __init__() invokedquot;)class Derived_Demo(Demo): def __new__(self): # 错误:__new__应接收cls作为第一个参数print(quot;Derived_Demo 的 __new__() 被调用quot;) # 隐式返回None,因为没有显式return def __init__(self): print(quot;Derived_Demo's __init__() invokedquot;)def main(): obj1 = Derived_Demo() print(fquot;obj1 is: {obj1}quot;) # 观察obj1的值 obj2 = Demo() print(fquot;obj2 is: {obj2}quot;) #观察obj2的值main()登录后复制
运行上述代码的输出:Derived_Demo's __new__() invokedDemo's __init__() invokedDemo's __new__() invokedobj1 is: Noneobj2 is: None 登录后复制
输出分析:
obj1 = Derived_Demo() 调用 Derived_Demo.__new__():打印“Derived_Demo 的 __new__()陷阱一:Derived_Demo.__new__方法没有显式返回任何对象。在Python中,如果一个函数没有显式返回,它会隐式返回None。因此,obj1最终被赋值为None。陷阱二:由于__new__返回了None而不是Derived_Demo的实例,Python解释器不会继续调用Derived_Demo.__init__。这就是为什么你没有看到“Derived_Demo的__init__()
obj2 = Demo() 调用Demo.__new__():陷阱三:在Demo.__new__内部,它手动调用了self.__init__(self)。此时,self实际上是Demo类(因为__new__的第一个参数是类)。
调用这个导致了Demo.__init__被执行,打印出“Demo's __init__() invoked”。这是一个反模式,__init__应该由Python解释器在__new__实例后自动调用。连接,打印“Demo's __new__() invoked”。陷阱四:与Derived_Demo.__new__类似,Demo.__new__也没有显式返回任何对象,因此它也式返回None。obj2最终也被赋值为None。
正确的__new__和__init__实现
为了避免上述问题,并保证__new__和__init__按预期工作,我们应该这样实现它们:class Demo: def __new__(cls, *args, **kwargs): # 正确:第一个参数是cls print(quot;Demo's __new__() invokedquot;) # 正确:通过super()调用父类(object)的__new__来创建实例实例 = super().__new__(cls) return instance # 正确:返回的实例 def __init__(self, value=None): # 可以有参数,用于初始化实例 print(fquot;Demo 的 __init__() invoked with value: {value}quot;) self.value = valueclass Derived_Demo(Demo): def __new__(cls, *args, **kwargs): # 正确:第一个参数是 cls print(quot;Derived_Demo 的 __new__() 被调用quot;) # 正确:通过super()调用父类(Demo)的__new__来创建返回实例 instance = super().__new__(cls) return instance # 正确:创建的实例 def __init__(self, name=None): # 可以有参数,用于初始化实例 super().__init__(name) # 调用父类的__init__ print(fquot;Derived_Demo's __init__() invoked with name: {name}quot;) self.name = namedef main(): print(quot;--- 创建 obj1 ---quot;) obj1 = Derived_Demo(name=quot;Alicequot;) print(fquot;obj1 is: {obj1}quot;) print(fquot;obj1.name: {obj1.name}quot;) print(fquot;obj1.value: {obj1.value}quot;) # 继承自Demo的属性 print(quot;\n--- 创建 obj2 ---quot;) obj2 = Demo(value=123) print(fquot;obj2 is: {obj2}quot;) print(fquot;obj2.value: {obj2.value}quot;)main()登录后复制
运行正确代码的输出:--- 创建 obj1 ---Derived_Demo 的 __new__() invokedDemo 的 __new__() inv
okedDemo 的 __init__() 调用的值: AliceDerived_Demo 的 __init__() 调用的名称: Aliceobj1 是: lt;__main__.Derived_Demo 对象位于 0x...gt;obj1.name: Aliceobj1.value: Alice--- 创建 obj2 ---Demo 的 __new__() 调用的 __init__() 调用的值: 123obj2 是: lt;__main__.Demo object at 0x...gt;obj2.value: 123登录后复制
正确代码分析:
obj1 = Derived_Demo(name="Alice"):首先调用Derived_Demo.__new__(cls, name="Alice")。Derived_Demo.__new__内部调用super().__new__(cls),这会继续向上调用Demo.__new__,最终由object.__new__(cls)创建Derived_Demo的实例。Derived_Demo.__new__返回这个实例。Python解释器接收到这个实例后,接着调用Derived_Demo.__init__(obj1, name="Alice")。Derived_Demo.__init__内部调用super().__init__(name),这会调用Demo.__init__来初始化父类部分。最终,obj1是一个完全初始化的Derived_Demo实例。
obj2 = Demo(value=123):首先调用Demo.__new__(cls, value=123)。Demo.__new__内部调用super().__new__(cls),由object.__new__(cls)创建Demo的实例。Demo.__new__返回这个实例。Python解释器接收到这个实例后,接着调用Demo.__init__(obj2, value=123)。最终,obj2是一个完全初始化的好的Demo实例。总结与最佳实践__new__是工厂,__init__是装修工:__new__负责生产出毛坯房(实例),__init__负责装修和配置这个毛坯房。继承重写__ne w__:除非你有特定的需求(如实现单例模式、自定义不可变对象等),否则通常不需要重写__new__方法。确保__new__返回实例:如果你重写了__new__,它必须返回一个有效的实例(通常是super().__new__(cls, ...)的结果)。如果返回None或非当前类的实例,后续的__init__将不会被调用。避免在__new__中手动调用__init__:Python解释器的工作。手动调用会导致不确定行为,并且可能中断正常的初始化流程。__new__的参数传递:__new__方法接收的参数(除了cls外部)会原封不动地传递给__init__。因此,__new__和__init__的签名(除了第一个参数)通常是匹配的。
理解__new__和__init__的正确用法对于高效编写健壮和的Python代码至关重要,尤其是在处理复杂的类结构和创建对象逻辑时。
以上就是深入理解Python文章中__new__和__init__的执行机制及常见陷阱的详细内容,更多请关注乐哥常识网其他相关!