Ir al contenido principal

Atributos y métodos de clase

Introducción

Los atributos y métodos son componentes fundamentales de las clases en Python, ya que definen las características (datos) y comportamientos (funciones) que tendrán los objetos creados a partir de ellas. Entender cómo definir y utilizar estos elementos es esencial para aprovechar al máximo la programación orientada a objetos. En este artículo, exploraremos en detalle los distintos tipos de atributos y métodos que pueden existir en una clase de Python, así como las mejores prácticas para su implementación.

Atributos de clase

Los atributos de clase son variables que pertenecen a la clase en sí misma, no a las instancias (objetos) creadas a partir de ella. Estos atributos son compartidos por todas las instancias de la clase.

class Empleado:
    # Atributo de clase
    empresa = "Tecnología Española S.A."
    
    def __init__(self, nombre, salario):
        # Atributos de instancia
        self.nombre = nombre
        self.salario = salario

# Creamos dos empleados
empleado1 = Empleado("Ana", 30000)
empleado2 = Empleado("Carlos", 35000)

# Accedemos al atributo de clase
print(Empleado.empresa)  # Output: Tecnología Española S.A.
print(empleado1.empresa)  # Output: Tecnología Española S.A.
print(empleado2.empresa)  # Output: Tecnología Española S.A.

# Si modificamos el atributo de clase, cambia para todos
Empleado.empresa = "Nueva Empresa S.L."
print(empleado1.empresa)  # Output: Nueva Empresa S.L.
print(empleado2.empresa)  # Output: Nueva Empresa S.L.

Características importantes de los atributos de clase:

  • Se definen fuera de cualquier método en el cuerpo de la clase
  • Se accede a ellos mediante NombreClase.atributo o instancia.atributo
  • Son útiles para definir constantes o valores compartidos por todas las instancias
  • Ocupan memoria una sola vez, independientemente del número de instancias

Atributos de instancia

Los atributos de instancia son variables que pertenecen a cada objeto (instancia) creado a partir de la clase. Cada instancia tiene su propia copia independiente de estos atributos.

class Coche:
    # Atributo de clase
    ruedas = 4
    
    def __init__(self, marca, modelo, color):
        # Atributos de instancia
        self.marca = marca
        self.modelo = modelo
        self.color = color
        self.kilometraje = 0  # Valor inicial
    
    def conducir(self, km):
        self.kilometraje += km
        print(f"El {self.marca} {self.modelo} ha recorrido {km} km")

# Creamos dos coches
mi_coche = Coche("Seat", "Ibiza", "rojo")
otro_coche = Coche("Renault", "Clio", "azul")

# Cada coche tiene sus propios atributos de instancia
print(mi_coche.marca)  # Output: Seat
print(otro_coche.marca)  # Output: Renault

# Modificamos un atributo de instancia
mi_coche.conducir(100)
print(mi_coche.kilometraje)  # Output: 100
print(otro_coche.kilometraje)  # Output: 0 (No se modifica)

Características importantes de los atributos de instancia:

  • Se definen dentro del método __init__ o en otros métodos usando self
  • Se accede a ellos mediante instancia.atributo
  • Cada instancia tiene su propia copia, independiente de otras instancias
  • Se pueden crear nuevos atributos para una instancia específica en cualquier momento

Métodos de instancia

Los métodos de instancia son funciones definidas dentro de una clase que operan sobre una instancia específica. Siempre reciben self como primer parámetro, que representa la instancia sobre la que actúan.

class Calculadora:
    def __init__(self, marca):
        self.marca = marca
        self.resultado = 0
    
    # Métodos de instancia
    def sumar(self, valor):
        self.resultado += valor
        return self.resultado
    
    def restar(self, valor):
        self.resultado -= valor
        return self.resultado
    
    def reiniciar(self):
        self.resultado = 0
        return self.resultado
    
    def mostrar_info(self):
        return f"Calculadora {self.marca} - Resultado actual: {self.resultado}"

# Creamos una calculadora
mi_calc = Calculadora("Casio")

# Usamos sus métodos
print(mi_calc.sumar(10))  # Output: 10
print(mi_calc.sumar(5))   # Output: 15
print(mi_calc.restar(7))  # Output: 8
print(mi_calc.mostrar_info())  # Output: Calculadora Casio - Resultado actual: 8
mi_calc.reiniciar()
print(mi_calc.resultado)  # Output: 0

Características importantes de los métodos de instancia:

  • Siempre reciben self como primer parámetro
  • Pueden acceder y modificar atributos de instancia usando self
  • Pueden llamar a otros métodos de la instancia usando self
  • Se invocan mediante instancia.metodo(argumentos)

Métodos de clase

Los métodos de clase están ligados a la clase en sí misma, no a sus instancias. Se definen utilizando el decorador @classmethod y reciben como primer parámetro cls (por convención), que representa la clase.

class Fecha:
    def __init__(self, dia, mes, año):
        self.dia = dia
        self.mes = mes
        self.año = año
    
    def mostrar(self):
        return f"{self.dia}/{self.mes}/{self.año}"
    
    @classmethod
    def desde_cadena(cls, cadena_fecha):
        # Crea una instancia de Fecha a partir de una cadena formato "dia-mes-año"
        dia, mes, año = map(int, cadena_fecha.split('-'))
        return cls(dia, mes, año)
    
    @classmethod
    def hoy(cls):
        # En un caso real importaríamos datetime para obtener la fecha actual
        return cls(21, 4, 2025)  # Fecha de ejemplo

# Creamos instancias de diferentes formas
fecha1 = Fecha(15, 3, 2025)
fecha2 = Fecha.desde_cadena("25-12-2025")
fecha3 = Fecha.hoy()

print(fecha1.mostrar())  # Output: 15/3/2025
print(fecha2.mostrar())  # Output: 25/12/2025
print(fecha3.mostrar())  # Output: 21/4/2025

Características importantes de los métodos de clase:

  • Se definen con el decorador @classmethod
  • Reciben cls como primer parámetro (la clase misma)
  • Pueden acceder a atributos de clase pero no a atributos de instancia
  • Pueden crear y devolver nuevas instancias de la clase
  • Se invocan mediante Clase.metodo(argumentos) o instancia.metodo(argumentos)
  • Son útiles para definir métodos alternativos de construcción

Métodos estáticos

Los métodos estáticos no tienen acceso ni a la instancia ni a la clase. Se definen usando el decorador @staticmethod y funcionan como funciones regulares dentro del espacio de nombres de la clase.

class Matematicas:
    @staticmethod
    def es_primo(numero):
        if numero <= 1:
            return False
        if numero <= 3:
            return True
        if numero % 2 == 0 or numero % 3 == 0:
            return False
        i = 5
        while i * i <= numero:
            if numero % i == 0 or numero % (i + 2) == 0:
                return False
            i += 6
        return True
    
    @staticmethod
    def factorial(n):
        if n == 0 or n == 1:
            return 1
        else:
            return n * Matematicas.factorial(n - 1)

# Usamos los métodos estáticos directamente desde la clase
print(Matematicas.es_primo(17))  # Output: True
print(Matematicas.es_primo(20))  # Output: False
print(Matematicas.factorial(5))  # Output: 120

# También podemos usar los métodos estáticos desde una instancia
mat = Matematicas()
print(mat.es_primo(23))  # Output: True
print(mat.factorial(4))  # Output: 24

Características importantes de los métodos estáticos:

  • Se definen con el decorador @staticmethod
  • No reciben automáticamente ningún primer argumento especial (self o cls)
  • No pueden acceder directamente a atributos de clase o de instancia
  • Son útiles para funciones relacionadas con la clase pero que no necesitan acceder a sus atributos
  • Se invocan mediante Clase.metodo(argumentos) o instancia.metodo(argumentos)

Acceso a atributos y métodos

En Python, por defecto, todos los atributos y métodos de una clase son públicos y pueden ser accedidos desde fuera de la clase. Sin embargo, existe una convención para indicar que ciertos elementos deberían considerarse privados:

class Persona:
    def __init__(self, nombre, edad):
        self.nombre = nombre  # Atributo público
        self._edad = edad     # Atributo "protegido" (convención)
        self.__dni = None     # Atributo "privado" (name mangling)
    
    def establecer_dni(self, numero):
        # Verificación simplificada (en un caso real sería más compleja)
        if len(numero) == 9:
            self.__dni = numero
            return True
        return False
    
    def obtener_dni(self):
        return "***" + self.__dni[-4:] if self.__dni else None

persona = Persona("Laura", 35)
print(persona.nombre)    # Acceso normal: Output: Laura
print(persona._edad)     # Técnicamente funciona, pero no es recomendable: Output: 35
# print(persona.__dni)   # Esto generaría un error

# Forma correcta de interactuar con atributos "privados"
persona.establecer_dni("123456789")
print(persona.obtener_dni())  # Output: ***6789

Notas sobre la convención de nombrado:

  • Un guion bajo al inicio (_atributo): Indica que el atributo es "protegido" y no debería accederse directamente desde fuera (aunque técnicamente es posible)
  • Dos guiones bajos al inicio (__atributo): Activa el "name mangling", renombrando internamente el atributo a _NombreClase__atributo, dificultando el acceso directo

Resumen

Los atributos y métodos son los bloques fundamentales de las clases en Python, que permiten definir tanto las características (datos) como los comportamientos (acciones) de los objetos. Hemos visto varios tipos:

  • Atributos de clase: Compartidos por todas las instancias, definidos directamente en la clase
  • Atributos de instancia: Propios de cada objeto, definidos generalmente dentro de __init__ con self
  • Métodos de instancia: Operan sobre una instancia específica, reciben self
  • Métodos de clase: Vinculados a la clase y no a instancias, usan @classmethod y reciben cls
  • Métodos estáticos: Funciones regulares dentro del espacio de nombres de la clase, usan @staticmethod

En el siguiente artículo, profundizaremos en el método constructor __init__, uno de los métodos especiales más importantes en la programación orientada a objetos en Python, que nos permite inicializar correctamente los objetos al momento de su creación.