📝 Repaso rápido: ¿Qué son las propiedades y atributos de clase en Python?
En Python, los atributos de clase son variables compartidas por todas las instancias de una clase. Son útiles para almacenar información común a todos los objetos. Vamos, los clásicos atributos definidos en una clase.

Por otro lado, las propiedades permiten controlar el acceso a los atributos de instancia, proporcionando una interfaz para obtener, establecer o eliminar valores con lógica personalizada.

Osea que más allá de los clásicos atributos de clase, tenemos los atributos de instancia, que se definen al momento de realizar una instancia, es decir, crear un objeto a partir de una clase.

🔍 Atributos de clase vs. atributos de instancia

Recordemos que instancia es la creación de un objeto a partir de una clase.

Los atributos de clase se definen directamente en la clase y son compartidos por todas las instancias.

En cambio, los atributos de instancia se definen dentro del método __init__ y son únicos para cada objeto. Veamos una comparación de ambos tipos de atributos:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Atributos de clase y atributos de instancia - OOP
# pythones.net

class Empleado:
    aumento = 1.05  # Atributo de clase

    def __init__(self, nombre, salario):
        self.nombre = nombre      # Atributo de instancia
        self.salario = salario    # Atributo de instancia

En este ejemplo, aumento es un atributo de clase que se aplica a todos los empleados, mientras que nombre y salario son atributos de instancia específicos de cada empleado.

Por lo que al crear un empleado lo haríamos de la siguiente manera:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Atributos de clase y atributos de instancia - OOP
# pythones.net

class Empleado:
    aumento = 1.05  # Atributo de clase

    def __init__(self, nombre, salario):
        self.nombre = nombre      # Atributo de instancia
        self.salario = salario    # Atributo de instancia
#Instanciar:
Marcos = Empleado('Marcos', 120000)
Marta = Empleado('Marta', 15000)
#Imprimir atributos de instancia
print(Marcos.nombre, Marcos.salario)
print(Marta.nombre, Marta.salario)
#Imprimir atributos de clase
print(Marcos.aumento)
print(Marta.aumento)

Resultado:
Marcos 120000
Marta 15000
1.05
1.05

Al momento de la instancia definimos los atributos de instancia, es decir, aquellos que en la clase están especificados dentro del __init__ como requeridos para la creación o inicialización del objeto. Y finalmente tenemos en cada objeto el atributo “aumento” que es un atributo de clase, por lo que será común a todos los objetos creados a partir de esa clase. Hasta aquí supongo ya habrás comprendido muy bien la diferencia, pero vamos a repasarlas:

📊 Diferencias clave en resumen

  • ✔️ Atributos de clase: compartidos entre todas las instancias.
  • ✔️ Atributos de instancia: únicos por objeto, definidos en __init__.
  • ✔️ Modificación desde instancia: crea un nuevo atributo de instancia que oculta el de clase.
  • ✔️ Modificación desde clase: afecta a todas las instancias (si no han sobrescrito el valor).

Fíjate en el siguiente ejemplo que modificamos estos atributos de clase y de instancia en cada objeto, siguiendo el código anterior:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Atributos de clase y atributos de instancia - OOP
# pythones.net

class Empleado:
    aumento = 1.05  # Atributo de clase

    def __init__(self, nombre, salario):
        self.nombre = nombre      # Atributo de instancia
        self.salario = salario    # Atributo de instancia
#Instanciar:
Marcos = Empleado('Marcos', 120000)
Marta = Empleado('Marta', 15000)
Juan = Empleado('Juan', 180000)
#Imprimir atributos de instancia
print(Marcos.nombre, Marcos.salario)
print(Marta.nombre, Marta.salario)
#Imprimir atributos de clase
print(Marcos.aumento)
print(Marta.aumento)

#Modificacion de atributos
#De clase:
Juan.aumento = 2.00
print (Juan.aumento)
print (Marta.aumento)

#De instancia:
Marta.salario = 170000
print(Marta.salario)

Resultado:
Marcos 120000
Marta 15000
1.05
1.05
2.0
1.05
170000

👥 Cómo se accede a los atributos de clase

Podemos acceder a un atributo de clase tanto desde la clase como desde una instancia. Sin embargo, si se modifica desde una instancia, se creará un atributo de instancia con el mismo nombre, sin alterar el valor compartido.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Atributos de clase y atributos de instancia - OOP
# pythones.net

class Empleado:
    aumento = 1.05  # Atributo de clase

    def __init__(self, nombre, salario):
        self.nombre = nombre      # Atributo de instancia
        self.salario = salario    # Atributo de instancia

emp1 = Empleado("Ana", 50000)
emp2 = Empleado("Luis", 60000)

print(emp1.aumento)  # Imprime 1.05
print(emp2.aumento)  # Imprime 1.05

Empleado.aumento = 1.10  # Cambia el valor del atributo de clase

print(emp1.aumento)  # Ahora imprime 1.10
print(emp2.aumento)  # También imprime 1.10

Resultado:
1.05
1.05
1.1
1.1

Como puedes ver, al modificar el atributo directamente desde la clase, se refleja en todas las instancias que no hayan sobrescrito ese valor. Por lo que si deseáramos modificar una atributo para todas las clases lo haríamos directamente llamando a la clase como objeto y asignando el nuevo valor, el cual se verá reflejado en todos los objetos que hayan sido creados a partir de ella.

⚠️ ¡Cuidado al modificar desde una instancia!

emp1.aumento = 1.20  # Esto crea un nuevo atributo de instancia

print(emp1.aumento)  # Imprime 1.20 (el de instancia)
print(emp2.aumento)  # Sigue siendo 1.10 (el de clase)

Es importante entender esta diferencia para evitar errores al manejar datos compartidos entre objetos.

Que modifiques un atributo en un objeto no hará que cambie para los demás, a menos que modifiques el atributo desde la clase, creo que ha quedado bastante claro!.

🏷️ Usando @property para definir propiedades

En Python, podemos usar el decorador @property para crear métodos que se comportan como atributos. Esto nos permite controlar el acceso a los valores, aplicando lógica si es necesario, sin cambiar la forma en que los llamamos.

La función integrada property() nos permite interceptar la escritura, lectura y borrado de los atributos, además de incorporar documentación sobre los mismos.

Volvamos a nuestro ejemplo de empleados. Supongamos que queremos que el email del empleado se genere automáticamente a partir del nombre, pero que se actualice si cambiamos ese nombre. Usaremos @property para lograrlo.

class Empleado:
    aumento = 1.05

    def __init__(self, nombre, salario):
        self.nombre = nombre
        self.salario = salario

    @property
    def email(self):
        return f"{self.nombre.lower()}@empresa.com"

Ahora podemos acceder al email como si fuera un atributo:

emp1 = Empleado("Carlos", 55000)
print(emp1.email)  # Imprime carlos@empresa.com

Si luego cambiamos el nombre del empleado, el email reflejará ese cambio automáticamente:

emp1.nombre = "Roberto"
print(emp1.email)  # Imprime roberto@empresa.com

Gracias al decorador @property, podemos mantener la lógica encapsulada dentro de un método, pero usando una sintaxis limpia y natural.

🔧 ¿Qué son getter, setter y deleter en las propiedades?

Cuando usamos el decorador @property en una clase, podemos controlar cómo se accede, modifica o elimina un atributo. Esto se logra usando tres métodos especiales:

  • Getter → Se encarga de interceptar la lectura del atributo. (get = obtener). Se define con @property.
  • Setter → Se encarga de interceptar cuando se escribe. (set = definir o escribir). Se define con @nombre.setter.
  • Deleter → Se encarga de interceptar cuando se borra. (delete = borrar). Se define con @nombre.deleter.
  • doc → Recibe una cadena para documentar el atributo. (doc = documentación)

🔹 Ejemplo 1 de propiedades de clase en python:

class Empleado:
    def __init__(self, nombre):
        self.nombre = nombre

    @property
    def nombre(self):
        return self.nombre

    @nombre.setter
    def nombre(self, valor):
        self.nombre = valor.title()  # Capitaliza el nombre

    @nombre.deleter
    def nombre(self):
        print("Eliminando nombre...")
        del self.nombre

Veamos cómo se comporta esta clase en la práctica:

emp = Empleado("ana")
print(emp.nombre)     # Ana
emp.nombre = "luis"   # Setter en acción
print(emp.nombre)     # Luis
del emp.nombre        # Se ejecuta el deleter

🔹 Ejemplo 2 de propiedades de clase en python:

Veamos ahora con otro ejemplo completo, con animalitos:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pythones.net

class Perros:
    def __init__(self, nombre, peso):
        self.nombre = nombre
        self.peso = peso

    @property
    def nombre(self):
        """Documentación del método nombre: Retorna el nombre del perro."""
        return self.nombre

    @nombre.setter
    def nombre(self, nuevo):
        print("Modificando nombre...")
        self.nombre = nuevo
        print("El nombre se ha modificado por")
        print(self.nombre)

    @nombre.deleter
    def nombre(self):
        print("Borrando nombre...")
        del self.nombre

    @property
    def peso(self):
        return self.peso

# Instanciamos
Tomas = Perros('Tom', 27)
print(Tomas.nombre)  # Imprime Tom
Tomas.nombre = 'Tomasito'  # Cambiamos el nombre usando setter
del Tomas.nombre  # Borramos el nombre usando deleter

Resultado:

Tom
Modificando nombre…
El nombre se ha modificado por
Tomasito
Borrando nombre…

📌 Resumen rápido:

Acción Decorador Uso
Leer @property print(obj.propiedad)
Escribir @propiedad.setter obj.propiedad = valor
Eliminar @propiedad.deleter del obj.propiedad

 

💡 Cuándo usar propiedades y atributos de clase en tus proyectos

Comprender la diferencia entre atributos de clase y de instancia no solo es útil, es crucial para evitar errores comunes al modelar datos. Por ejemplo, los atributos de clase son ideales para valores estándar, tasas fijas o configuraciones predeterminadas que se aplican globalmente, mientras que las propiedades son una excelente forma de encapsular lógica sin exponerla directamente al exterior.

Además, usar @property te da la flexibilidad de cambiar la implementación interna de una clase sin afectar el código que la utiliza, lo que es una gran ventaja en proyectos que evolucionan constantemente.

📝 Repaso rápido: Propiedades de clase en python
  • 🔹 Las propiedades de clase se definen fuera del constructor y son compartidas entre todas las instancias.
  • 🔹 Se pueden modificar directamente desde la clase o desde una instancia.
  • 🔹 El decorador @property permite crear métodos que se comportan como atributos, muy útil para definir valores derivados o controlados.
  • 🔹 Usar @property mejora la legibilidad y el mantenimiento del código.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio