¿Qué es __init__ en Python? [With Examples]

¿Desea comenzar con el diseño orientado a objetos en Python? Dé los primeros pasos hoy aprendiendo sobre el método __init__ de Python.

En este tutorial, repasaremos los conceptos básicos de las clases y los objetos de Python, luego procederemos a aprender sobre el método __init__.

Al final de este tutorial, podrá responder las siguientes preguntas:

  • ¿Qué son las variables de instancia o los atributos de instancia?
  • ¿Cómo ayuda el método init a inicializar los atributos de la instancia?
  • ¿Cómo podemos establecer valores predeterminados para los atributos?
  • ¿Cómo podemos usar métodos de clase como constructores para crear objetos?

Empecemos.

Objetos y clases de Python

Las clases son fundamentales para la programación orientada a objetos en Python. Podemos crear una clase y definir atributos y métodos para unir datos y funcionalidad asociada.

Una vez que hemos creado una clase, podemos usarla como modelo (o plantilla) para crear objetos (instancias).

👩‍🏫¡Tiempo de ejemplo! Vamos a crear una clase de empleado donde cada objeto de esta clase tenga los siguientes atributos:

  • full_name: el nombre completo del empleado en el formato firstName lastName
  • emp_id: la identificación del empleado
  • departamento: el departamento al que pertenecen
  • experiencia: el número de años de experiencia que tienen

¿Qué quiere decir esto? 🤔

Cada empleado individual será una instancia u objeto de la clase Empleado. Y cada objeto tendrá su propio valor para full_name, emp_id, departamento y experiencia.

Estos atributos también se denominan variables de instancia y utilizaremos los términos atributos y variables de instancia indistintamente.

Vamos a empezar a agregar atributos en un momento. Por ahora creamos una clase de empleado así:

class Employee:
    pass

Usar pass (como marcador de posición) nos ayuda a evitar errores cuando ejecutamos el script.

Aunque la versión actual de la clase Empleado no es muy útil, sigue siendo una clase válida. Entonces podemos crear objetos de la clase Empleado:

employee_1 = Employee()

print(employee_1)
#Output: <__main__.Employee object at 0x00FEE7F0>

También podemos agregar atributos e inicializarlos con valores como se muestra:

employee_1.full_name="Amy Bell"
employee_1.department="HR"

Pero este enfoque para agregar atributos de instancia es ineficiente y propenso a errores. Además, esto no permite el uso de la clase como plantilla para crear objetos. Aquí es donde ayuda el método __init__.

Comprender el papel del método __init__ en una clase de Python

Deberíamos poder inicializar las variables de instancia al instanciar un objeto y el método __init__ nos ayuda a hacerlo. El método __init__ se llama cada vez que se crea un nuevo objeto de la clase para inicializar los valores de las variables de instancia.

Si ha programado en un lenguaje como C++, verá que el método __init__ funciona de manera similar a los constructores.

Definición del método __init__

Agreguemos el método __init__ a la clase Employee:

class Employee:
    def __init__(self, full_name,emp_id,department,experience):
        self.full_name = full_name
        self.emp_id = emp_id
        self.department = department
        self.experience = experience

El parámetro self hace referencia a la instancia de la clase y el atributo self inicializa el atributo de la instancia en el valor del lado derecho.

Ahora podemos crear objetos así:

employee_2 = Employee('Bella Joy','M007','Marketing',3)
print(employee_2)
# Output: <__main__.Employee object at 0x017F88B0>

Cuando imprimimos los objetos de los empleados, no obtenemos ninguna información útil excepto la clase a la que pertenecen. Agreguemos un método __repr__ que defina una cadena de representación para la clase:

 def __repr__(self):
        return f"{self.full_name},{self.emp_id} from {self.department} with {self.experience} years of experience."

Agregando el __repr__ a la clase Empleado, tenemos:

class Employee:
    def __init__(self, full_name,emp_id,department,experience):
        self.full_name = full_name
        self.emp_id = emp_id
        self.department = department
        self.experience = experience
    
     def __repr__(self):
        return f"{self.full_name},{self.emp_id} from {self.department} with {self.experience} years of experience."

Ahora los objetos empleados tienen una cadena de representación útil:

print(employee_2)
# Output: Bella Joy,M007 from Marketing with 3 years of experience.

Algunas convenciones

Antes de continuar, aquí hay un par de notas:

  • Usamos self como el primer parámetro en el método __init__ para hacer referencia a la instancia de la clase en sí y usamos self.attribute_name para inicializar los diversos atributos. Usar self es la convención preferida (aunque puede usar cualquier otro nombre).
  • Al definir el método __init__, establecemos los nombres de los parámetros en las definiciones de __init__ para que coincidan con los nombres de los atributos. Esto mejora la legibilidad.

Cómo agregar valores predeterminados para atributos

En el ejemplo que hemos codificado hasta ahora, se requieren todos los atributos. Lo que significa que la creación de objetos es exitosa solo si pasamos los valores de todos los campos al constructor.

Intente crear una instancia de un objeto de la clase Empleado sin pasar el valor del atributo de experiencia:

employee_3 = Employee('Jake Lee','E001','Engineering')

Obtendrá el siguiente error:

Traceback (most recent call last):
  File "main.py", line 22, in <module>
    employee_3 = Employee('Jake Lee','E001','Engineering')
TypeError: __init__() missing 1 required positional argument: 'experience'

Pero si desea que ciertos atributos sean opcionales, puede hacerlo proporcionando valores predeterminados para esos atributos al definir el método __init__.

Aquí proporcionamos un valor predeterminado de 0 para el atributo de experiencia:

class Employee:
    def __init__(self, full_name,emp_id,department,experience=0):
        self.full_name = full_name
        self.emp_id = emp_id
        self.department = department
        self.experience = experience
    
     def __repr__(self):
        return f"{self.full_name},{self.emp_id} from {self.department} with {self.experience} years of experience."

El objeto employee_3 se crea sin valor para el atributo de experiencia; el valor predeterminado de 0 se utiliza para la experiencia.

employee_3 = Employee('Jake Lee','E001','Engineering')
print(employee_3.experience)
# Output: 0

Constructores de clase alternativos usando métodos de clase

Hasta ahora solo hemos visto cómo definir el método __init__ y establecer valores predeterminados para los atributos cuando sea necesario. También sabemos que necesitamos pasar los valores de los atributos requeridos en el constructor.

A veces, sin embargo, los valores de estas variables de instancia (o atributos) pueden estar disponibles en una estructura de datos diferente, como una tupla, un diccionario o una cadena JSON.

¿Asi que que hacemos?

Tomemos un ejemplo. Supongamos que tenemos valores de la variable de instancia en un diccionario de Python:

dict_fanny = {'name':'Fanny Walker','id':'H203','dept':'HR','exp':2}

Podemos aprovechar el diccionario y obtener todos los atributos de esta manera:

name = dict_fanny['name']
id = dict_fanny['id']
dept = dict_fanny['dept']
exp = dict_fanny['exp']

Después de esto, puede crear un objeto pasando estos valores al constructor de la clase:

employee_4 = Employee(name, id, dept, exp)
print(employee_4)
# Output: Fanny Walker,H203 from HR with 2 years of experience.

Recuerde: debe hacer esto para cada nuevo objeto que cree. Este enfoque es ineficiente y definitivamente podemos hacerlo mejor. ¿Pero cómo?

En Python, podemos usar métodos de clase como constructores para crear objetos de la clase. Para crear un método de clase usamos el decorador @classmethod.

Definamos un método que analice el diccionario, obtenga los valores de las variables de instancia y los use para construir objetos Employee.

    @classmethod
    def from_dict(cls,data_dict):
        full_name = data_dict['name']
        emp_id = data_dict['id']
        department = data_dict['dept']
        experience = data_dict['exp']
        return cls(full_name, emp_id, department, experience)

Cuando necesitamos crear objetos usando datos del diccionario, podemos usar el método de clase from_dict().

💡 Observe el uso de cls en el método class en lugar de self. De la misma manera que usamos self para referirnos a la instancia, cls se usa para referirnos a la clase. Además, los métodos de clase están vinculados a la clase y no a los objetos.

Entonces, cuando llamamos al método de clase from_dict() para crear objetos, lo llamamos en la clase Empleado:

emp_dict = {'name':'Tia Bell','id':'S270','dept':'Sales','exp':3}
employee_5 = Employee.from_dict(emp_dict)
print(employee_5)
# Output: Tia Bell,S270 from Sales with 3 years of experience.

Ahora, si tenemos un diccionario para cada uno de los n empleados, podemos usar el método de clase from_dict() como constructor para instanciar objetos, sin tener que recuperar los valores de las variables instantáneas del diccionario.

📝Una nota sobre las variables de clase

Aquí definimos el método de clase que está vinculado a la clase y no a las instancias individuales. Similar a los métodos de clase, también podemos tener variables de clase.

Al igual que los métodos de clase, las variables de clase están vinculadas a la clase y no a una instancia. Cuando un atributo toma un valor fijo para todas las instancias de la clase, podemos considerar definirlas como variables de clase.

preguntas frecuentes

1. ¿Por qué necesita el método __init__ en Python?

El método __init__ en una definición de clase nos permite inicializar los atributos o variables de instancia de todas las instancias de la clase. El método __init__ se llama cada vez que se crea una nueva instancia de la clase.

2. ¿Puede tener varios métodos __init__ en una clase de Python?

El objetivo de tener varios métodos __init__ en una clase de Python es proporcionar varios constructores que instancian objetos. Pero no puede definir múltiples métodos __init__. Si define varios métodos __init__, la segunda implementación y la más reciente sobrescribirán la primera. Sin embargo, puede usar el decorador @classmethod para definir métodos de clase que se pueden usar como constructores para crear instancias de objetos.

3. ¿Qué sucede si no define el método __init__ en una clase?

Si no define el método __init__, aún puede crear instancias de objetos. Sin embargo, deberá agregar variables de instancia manualmente y asignar valores a cada una de ellas. No podrá pasar los valores de las variables de instancia en el constructor. Esto no solo es propenso a errores, sino que también anula el propósito de tener la clase como modelo a partir del cual podemos crear instancias de objetos.

4. ¿Puede tener valores predeterminados para los argumentos en el método __init__?

Sí, es posible proporcionar valores predeterminados para uno o más atributos al definir el método __init__. Proporcionar valores predeterminados ayuda a que esos atributos sean opcionales en el constructor. Los atributos toman los valores predeterminados cuando no pasamos los valores de estos atributos en el constructor.

5. ¿Puedes modificar atributos fuera del método __init__?

Sí, siempre puede actualizar el valor de un atributo fuera del método __init__. También puede agregar nuevos atributos dinámicamente a una instancia después de haber creado la instancia en particular.

Conclusión

En este tutorial, aprendimos a usar el método __init__ para inicializar los valores de las variables de instancia. Aunque esto es sencillo, puede ser repetitivo, especialmente cuando tiene muchos atributos.

Si está interesado, puede explorar el módulo de clases de datos. En Python 3.7 y versiones posteriores, puede usar el módulo de clases de datos integrado para crear clases de datos que almacenen datos. Además de las implementaciones predeterminadas de __init__ y otros métodos de uso común; vienen con muchas características geniales para sugerencias de tipo, valores predeterminados complejos y optimización.

A continuación, obtenga más información sobre if __name__==’__main__’ en Python.