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.
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.
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__
:
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.
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.
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
).
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)
__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__()
.