魔法方法 __init__ 与 __new__ 的区别与使用场景
前言在 Python 中魔法方法也叫特殊方法以双下划线开头和结尾例如 __init__、__new__、__str__ 等。它们赋予了类许多“隐形”的能力让我们能够像操作内置类型一样操作自定义对象。当谈到对象创建时__new__ 和 __init__ 是两个最容易混淆的魔法方法。很多人以为 __init__ 就是构造函数其实严格来说__new__ 才是真正的“构造”方法而 __init__ 是“初始化”方法。本文将深入剖析 __new__ 与 __init__ 的区别、调用顺序、参数以及各自的使用场景通过代码示例帮助你彻底掌握它们。一、 对象创建的过程在 Python 中当我们执行 obj MyClass(arg1, arg2) 时解释器内部经历了以下两个步骤1. 调用 __new__分配内存创建一个新的实例对象通常返回该实例。2. 调用 __init__如果 __new__ 返回的是 MyClass 的实例则自动调用 __init__ 对其进行初始化否则 __init__ 不会被调用。整个过程可以理解为python def __call__(cls, *args, **kwargs): instance cls.__new__(cls, *args, **kwargs) if isinstance(instance, cls): cls.__init__(instance, *args, **kwargs) return instance所以 __new__ 负责“造出”对象__init__ 负责“装修”对象。二、 __new__ 详解2.1 定义与参数__new__ 是一个静态方法但不需要显式声明它的第一个参数是类 cls后面跟的是传递给构造器的参数。python class MyClass: def __new__(cls, *args, **kwargs): print(__new__ 被调用) instance super().__new__(cls) # 调用父类的 __new__ return instance2.2 返回值__new__ 必须返回一个实例对象。通常返回的是本类cls的实例但也可以返回其他类的实例。如果返回的是 cls 的实例Python 会自动调用 __init__ 初始化它。如果返回的是其他类的实例则不会调用 __init__因为对象类型不匹配。2.3 主要作用分配内存创建并返回一个新对象类似其他语言的 new 关键字。控制实例创建可以实现单例模式、缓存实例、返回已有实例等。三、 __init__ 详解3.1 定义与参数__init__ 的第一个参数是实例 self后面跟的是构造器传入的参数。它不返回任何值或者说返回 None。python class MyClass: def __init__(self, value): print(__init__ 被调用) self.value value3.2 主要作用初始化实例属性为实例设置初始状态进行必要的校验或默认值处理。3.3 注意事项__init__ 只有在 __new__ 返回的是本类实例时才会被调用。如果 __new__ 返回了非本类实例__init__ 不会执行。四、__new__ 与 __init__ 的区别对比特性__new____init__调用时机在对象创建时最先调用在 __new__ 返回本类实例后调用参数第一个参数是类 cls其余为构造参数第一个参数是实例 self其余为构造参数返回值必须返回一个实例可以是任意类的实例必须返回 None不返回或返回 None主要职责创建并返回一个新实例控制实例生成初始化已创建的实例设置属性等使用场景单例模式、不可变类型子类、缓存等常规的属性初始化是否必须实现通常不需要除非需要控制实例创建通常都会实现用来初始化属性五、 典型使用场景5.1 单例模式单例模式确保一个类只有一个实例。通过重写 __new__ 来控制实例的创建。python class Singleton: _instance None def __new__(cls, *args, **kwargs): if cls._instance is None: print(创建新实例) cls._instance super().__new__(cls) else: print(返回已有实例) return cls._instance def __init__(self, value): self.value value s1 Singleton(10) s2 Singleton(20) print(s1 is s2) # True print(s1.value, s2.value) # 20 20因为__init__每次都会调用所以第二次覆盖了value注意上面的例子中 __init__ 每次都会执行导致第二次修改了 value。如果希望保持第一次的值可以在 __init__ 中加入判断是否已初始化。python class Singleton: _instance None _initialized False def __new__(cls, *args, **kwargs): if cls._instance is None: cls._instance super().__new__(cls) return cls._instance def __init__(self, value): if not self._initialized: self.value value self._initialized True5.2 不可变类型的子类当继承不可变类型如int、str、tuple时__new__ 是修改值的唯一机会因为不可变对象在创建后无法修改。python class PositiveInteger(int): 只接受正整数的整数子类 def __new__(cls, value): if value 0: raise ValueError(必须是正整数) # 通过 super().__new__ 创建 int 实例 return super().__new__(cls, value) def __init__(self, value): # 这里 value 已经不可变无需额外操作 super().__init__() try: p PositiveInteger(10) print(p) # 10 n PositiveInteger(-5) # 抛出 ValueError except ValueError as e: print(e)5.3 缓存实例对象池有时我们希望重复使用相同参数的实例避免重复创建。可以通过 __new__ 实现缓存。python class Student: _cache {} def __new__(cls, name): if name in cls._cache: print(f从缓存返回 {name}) return cls._cache[name] else: print(f创建新对象 {name}) instance super().__new__(cls) cls._cache[name] instance return instance def __init__(self, name): self.name name s1 Student(张三) s2 Student(张三) s3 Student(李四) print(s1 is s2) # True print(s1 is s3) # False5.4 返回其他类的实例__new__ 可以返回任何类的实例这在某些元编程场景中有用。python class A: def __new__(cls): return B() def __init__(self): print(A 的 __init__ 不会执行) class B: def __init__(self): print(B 的 __init__ 执行) a A() # 输出 B 的 __init__ 执行 print(type(a)) # class __main__.B六、 注意事项与常见误区1. __new__ 必须返回一个实例否则创建的对象为 None。2. __init__ 不能有返回值除了 None否则会引发 TypeError。3. 如果没有重写 __new__默认会调用 object.__new__。4. 重写 __new__ 时通常使用 super().__new__(cls) 来创建实例避免无限递归。5. __init__只有在 __new__ 返回本类实例时才会被调用返回其他类实例时不会调用。6. 对于不可变类型的子类通常只需要重写 __new__因为 __init__ 在不可变对象创建后不会被调用但实际仍会调用只是不可变对象在 __new__ 中已经确定了值。七、总结__new__ 是真正的“构造方法”负责创建并返回实例对象常用于控制实例的生成过程。__init__ 是“初始化方法”负责设置实例的初始状态是最常见的魔法方法。两者在对象创建过程中按顺序执行__new__ →如果返回本类实例→ __init__。希望本文能对你有所帮助欢迎在评论区留言讨论
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2469808.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!