ProgramaciónDesarrollador Senior de Python

¿Cómo funciona el método __new__ en Python y en qué se diferencia de __init__? ¿Cuándo y por qué es necesario sobreescribir __new__ en lugar de __init__? Proporcione un ejemplo detallado de uso.

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

En Python, la creación de un objeto de clase es un proceso de dos etapas:

  1. Primero se llama al método estático __new__, que se encarga de asignar y devolver una nueva instancia de la clase.
  2. Luego se llama a __init__, que inicializa el objeto ya creado.

Diferencias:

  • __new__ — creación (se puede devolver cualquier nuevo objeto; parámetro obligatorio — clase).
  • __init__ — inicialización (trabaja con self, una instancia ya asignada).
  • Habitualmente, __new__ se sobreescribe para herederos de tipos inmutables (tuple, str, int), creación de singletons y el patrón "fábrica".

Ejemplo de uso:

class MyStr(str): def __new__(cls, value): print("__new__ llamado") instance = super().__new__(cls, value.upper()) # se cambia el valor antes de la creación return instance def __init__(self, value): print("__init__ llamado", value) s = MyStr('abc') # __new__ llamado -> __init__ llamado abc print(s) # 'ABC'

Pregunta tramposa.

¿Se puede inicializar un objeto inmutable a través de __init__, si la clase no tiene implementado __new__?

Respuesta con ejemplo:

¡No! Por ejemplo, para tipos inmutables (como str, int, tuple) cualquier modificación a los campos/valores debe hacerse en __new__, de lo contrario, no se puede cambiar el valor en __init__.

class MyTuple(tuple): def __init__(self, items): print(f'__init__! {items}') def __new__(cls, items): print(f'__new__! {items}') return super().__new__(cls, map(str, items)) t = MyTuple([1, 2, 3]) print(t) # ('1', '2', '3')

Ejemplos de errores reales debido a la falta de conocimiento sobre los matices del tema


Historia

En un gran proyecto en Django, implementaron la herencia de str para almacenar tipos de cadenas especiales. Intentaron modificar el valor en __init__ — pero el resultado no cambiaba, ya que str es inmutable. Lo corrigieron solo después de estudiar y sobreescribir __new__.


Historia

Al implementar el patrón Singleton, olvidaron implementar la lógica para devolver la instancia existente en __new__: varias llamadas a la clase seguían creando nuevos objetos, lo que llevaba a la fragmentación de memoria.


Historia

En una biblioteca para la serialización de datos, al almacenar en caché utilizaron una clase con __init__ sobreescrito, pero no se dieron cuenta de que para la caché era necesario devolver el mismo objeto a través de __new__. Como resultado, la caché se rompía en cada nueva llamada, porque se creaban diferentes objetos.