¿Qué puede hacer `__init__` que` __new__` no pueda?

En Python, __new__ se usa para inicializar tipos inmutables y __init__ normalmente inicializa tipos mutables. Si __init__ se eliminó del idioma, ¿qué no se podría hacer (fácilmente)?

Por ejemplo,

 class A: def __init__(self, *, x, **kwargs): super().__init__(**kwargs) self.x = x class B(A): def __init__(self, y=2, **kwargs): super().__init__(**kwargs) self.y = y 

Podría reescribirse utilizando __new__ esta manera:

 class A_N: def __new__(cls, *, x, **kwargs): obj = super().__new__(cls, **kwargs) obj.x = x return obj class B_N(A_N): def __new__(cls, y=2, **kwargs): obj = super().__new__(cls, **kwargs) obj.y = y return obj 

Aclaración para el scope de la pregunta: Esta no es una pregunta sobre cómo se __init__ y __new__ o cuál es la diferencia entre ellos. Esta es una pregunta sobre qué sucedería si __init__ se eliminara del idioma. ¿Se rompería algo? ¿Sería algo mucho más difícil o imposible de hacer?

Todo lo que puede hacer en __init__ también se puede hacer en __new__ .

Entonces, ¿por qué usar __init__ ?
Porque no tienes que almacenar la instancia en la variable ( obj en tu código de ejemplo), y luego molestarte en devolverla. Puede concentrarse en lo que realmente desea hacer: inicializar un objeto mutable.

Nota sobre la diferencia entre __new__ y __init__

Antes de explicar la funcionalidad que falta, volvamos a la definición de __new__ y __init__ :

__new__ es el primer paso de la creación de la instancia. Se llama primero y es responsable de devolver una nueva instancia de su clase.

Sin embargo, __init__ no devuelve nada ; solo es responsable de inicializar la instancia una vez creada.

Consecuencias de reemplazar __init__ a __new__

Principalmente perderías en flexibilidad. __new__ and muchos dolores de cabeza semánticos y una separación __new__ and de la inicialización y la construcción (uniéndose a __new__ and iniciamos unir la construcción y la inicialización en un solo paso …). Echemos un vistazo al fragmento a continuación:

 class A(object): some_property = 'some_value' def __new__(cls, *args, **kwargs): obj = object.__new__(cls, *args, **kwargs) obj.some_property = cls.some_property return obj class B(A): some_property = 2 def __new__(cls, *args, **kwargs): obj = super(B, cls).__new__(cls) return obj 

Consecuencias de mover acciones __new__ a __new__ :

  1. Inicializa B antes de A : Cuando estás utilizando el método __new__ lugar de __init__ tu primer paso para crear una nueva instancia de B es llamar a A.__new__ como efecto secundario, no puedes inicializar B antes de que se A.__new__ A (accede y asigna algunas propiedades a la nueva instancia de B) . El uso de __init__ le da tal flexibilidad.

  2. Control suelto en el orden de inicialización: imaginemos que usted ha heredado B_N de dos clases ( A_N1 , A_N2 ), ahora perdería el control del orden de inicialización de una nueva instancia de B_N (¿cuál es el orden en el que va a inicializar las instancias? Podría ser importa … lo que es raro.

  3. Lío de propiedades y métodos: perdería el acceso a A.some_property ( cls sería igual a B mientras A.some_property instancia de la nueva instancia de B Sin embargo, el acceso directo a A.some_property es posible, pero supongo que es al menos extraño acceder a las propiedades dentro de la clase nombre de la clase y no utilizando classmethods ).

  4. No puede reiniciar una instancia existente sin crear una nueva o una lógica especial de implementación para esto (gracias a @platinhom por idea)

¿Qué puede hacer __init__ que __new__ no puede?

No hay acciones que no se puedan realizar en __new__ y pueden en __init__ , porque las acciones que __init__ realizan son un subconjunto de las acciones que puede realizar __new__ .

Un momento interesante de Python Docs, Pickling y despojo de instancias de clase normales # objeto. obtener información sobre cuándo __init__ podría ser útil:

Cuando se deselecciona una instancia de clase encurtida, su método init () normalmente no se invoca.

Por When to use __new__ vs. __init__

__new__ es el primer paso de la creación de la instancia. Se llama primero y es responsable de devolver una nueva instancia de su clase. En contraste, __init__ no devuelve nada; solo es responsable de inicializar la instancia una vez creada.

También la clase de clase es type , y el type.__call__() se implementa de manera similar a la siguiente, refiérase a la descripción anterior:

 def __call__(cls, *args, **kwargs): obj = cls.__new__(cls, *args, **kwargs) if isinstance(obj, cls): obj.__init__(*args, **kwargs) return obj 

Sabemos que __init__() simplemente hacer una parte de __new__() puede hacer cualquier cosa a ese objeto antes de que se devuelva el objeto.

En general, no debería necesitar anular __new__ menos que esté subclasificando un tipo inmutable como str, int, unicode o tuple.

Por lo tanto, no es bueno eliminar __init__ del lenguaje, y es mejor usar siempre __init__() mejor que usar __new__() .

Aquí hay una historia de Low-level constructors and __new__() de Low-level constructors and __new__() .