Python常见面试题007. 谈谈Python中__init__和__new__的区别

时间:2022-06-25 00:43:14
  • python中关于dunder method双下方法,或magic method魔术方法的描述多在

    https://docs.python.org/zh-cn/3.9/reference/datamodel.html#special-method-names
    

实例演示特性

  1. __new__是个静态方法(要看源码了),__init__是实例方法

  2. __new__需要返回一个实例,如果不返回,新实例的__init__不会被执行

    class Person:
        def __new__(cls, *args, **kwargs):
            return 'new'
    
        def __init__(self):
            print('calling init')
    
    
    wuxianfeng = Person()  # 什么都不会打印,如果注释掉new方法就可以了
    
    
    #当然你也可以这样写
    class Person:
        def __new__(cls, *args, **kwargs):
            instance = super().__new__(cls, *args, **kwargs)
            return instance
    
        def __init__(self):
            print('calling init')
    
    
    wuxianfeng = Person() # 也是会打印的
    
    
  3. __init__只能返回None

    class Person:
        def __init__(self):
            print('calling init')
            return 1
    
    wuxianfeng = Person()
    # 报错 TypeError: __init__() should return None, not 'int'
    # 但是也会输出calling init
    
    
  4. __new__在__init__之前

    class Person:
        def __new__(cls, *args, **kwargs):
            print('calling new')
            instance = super().__new__(cls, *args, **kwargs)
            return instance
    
        def __init__(self):
            print('calling init')
    
    
    wuxianfeng = Person()  # 先打印 calling new 再打印 calling init
    
    
    wuxianfeng = Person()
    
    # 你可以理解为这样的一个过程(参考https://www.bilibili.com/video/BV1b84y1e7hG)
    
    wuxianfeng = __new__(Person)
    
    __init__(wuxianfeng )
    
    
  5. 如果我们在建立一个object的过程中,传入了一些参数,那么这些参数既会被传入到new里也会被传入到init里

    class Person:
        def __new__(cls, x):
            print(f'calling new,and {x}')
            instance = super().__new__(cls)
            return instance
    
        def __init__(self,x):
            self.x = x
            print(f'calling init,and get {self.x}')
    
    
    wuxianfeng = Person('wxf')
    # calling new,and wxf
    # calling init,and get wxf
    
    
    如果在上面的代码中你去掉__new__(cls, x):中的,x,IDE会给你提示:签名不兼容,而且是双向的,new提示不兼容init,init提示不兼容new
    
    
    
    

  • 说在最后
    • __init__是大家都必须要掌握的,也是用的最多的魔术方法了
    • __new__是从一个class建立一个object的过程,如果你要更改这个过程(比如单例中,你在建立object之前要判断是否有建立过obj,从而确定自己是否要再建立),你就可以考虑用__new__
    • __init__是有了上面的__new__出来的object后对它进行初始化的一个过程
    • metaclass里面对new是用的相对多一些

官方说明

  • __new__中文部分

    object.__new__(cls[, ...])
    调用以创建一个 cls 类的新实例。__new__() 是一个静态方法 (因为是特例所以你不需要显式地声明),它会将所请求实例所属的类作为第一个参数。其余的参数会被传递给对象构造器表达式 (对类的调用)。__new__() 的返回值应为新对象实例 (通常是 cls 的实例)。
    
    典型的实现会附带适宜的参数使用 super().__new__(cls[, ...]),通过超类的 __new__() 方法来创建一个类的新实例,然后根据需要修改新创建的实例再将其返回。
    
    如果 __new__() 在构造对象期间被发起调用并且它返回了一个 cls 的实例,则新实例的 __init__() 方法将以 __init__(self[, ...]) 的形式被发起调用,其中 self 为新实例而其余的参数与被传给对象构造器的参数相同。
    
    如果 __new__() 未返回一个 cls 的实例,则新实例的 __init__() 方法就不会被执行。
    
    __new__() 的目的主要是允许不可变类型的子类 (例如 int, str 或 tuple) 定制实例创建过程。它也常会在自定义元类中被重载以便定制类创建过程。
    
  • __new__英文部分

    object.__new__(cls[, ...])
    Called to create a new instance of class cls. __new__() is a static method (special-cased so you need not declare it as such) that takes the class of which an instance was requested as its first argument. The remaining arguments are those passed to the object constructor expression (the call to the class). The return value of __new__() should be the new object instance (usually an instance of cls).
    
    Typical implementations create a new instance of the class by invoking the superclass’s __new__() method using super().__new__(cls[, ...]) with appropriate arguments and then modifying the newly-created instance as necessary before returning it.
    
    If __new__() is invoked during object construction and it returns an instance of cls, then the new instance’s __init__() method will be invoked like __init__(self[, ...]), where self is the new instance and the remaining arguments are the same as were passed to the object constructor.
    
    If __new__() does not return an instance of cls, then the new instance’s __init__() method will not be invoked.
    
    __new__() is intended mainly to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation. It is also commonly overridden in custom metaclasses in order to customize class creation.
    
  • __init__中文部分

    在实例 (通过 __new__()) 被创建之后,返回调用者之前调用。其参数与传递给类构造器表达式的参数相同。一个基类如果有 __init__() 方法,则其所派生的类如果也有 __init__() 方法,就必须显式地调用它以确保实例基类部分的正确初始化;例如: super().__init__([args...]).
    
    因为对象是由 __new__() 和 __init__() 协作构造完成的 (由 __new__() 创建,并由 __init__() 定制),所以 __init__() 返回的值只能是 None,否则会在运行时引发 TypeError。
    
  • __init__英文部分

    object.__init__(self[, ...])
    Called after the instance has been created (by __new__()), but before it is returned to the caller. The arguments are those passed to the class constructor expression. If a base class has an __init__() method, the derived class’s __init__() method, if any, must explicitly call it to ensure proper initialization of the base class part of the instance; for example: super().__init__([args...]).
    
    Because __new__() and __init__() work together in constructing objects (__new__() to create it, and __init__() to customize it), no non-None value may be returned by __init__(); doing so will cause a TypeError to be raised at runtime.