Lección Python

Contenido de la lección

Introducción

Contenido de la lección.

Objetivos de aprendizaje
  • Objetivo 1
  • Objetivo 2
  • Objetivo 3

Objetivos de Aprendizaje

Al finalizar este tema, serás capaz de:
  • Comprender el concepto y propósito de los métodos especiales en Python
  • Implementar métodos especiales fundamentales como __init__, __str__ y __repr__
  • Aplicar sobrecarga de operadores para crear clases más intuitivas y funcionales
  • Desarrollar clases que se integren naturalmente con las funciones built-in de Python

📚 Conceptos Fundamentales de Métodos Especiales

Los métodos especiales son la base del modelo de datos de Python y permiten que las clases personalizadas se comporten como tipos de datos nativos. El método __init__ es el constructor que se ejecuta al crear una instancia. __str__ define la representación de cadena para usuarios finales, mientras que __repr__ proporciona una representación técnica para desarrolladores. Estos métodos se invocan automáticamente cuando se utilizan operadores o funciones específicas. La convención de dobles guiones bajos indica que estos métodos tienen un significado especial para el intérprete de Python. Es importante distinguir entre __str__ (legible para humanos) y __repr__ (no ambigua para desarrolladores). Una buena práctica es que __repr__ devuelva una expresión que pueda recrear el objeto.

Clase básica con métodos especiales fundamentales Copiar
class Persona:
    def __init__(self, nombre, edad):
        self.nombre = nombre
        self.edad = edad
    
    def __str__(self):
        return f'{self.nombre}, {self.edad} años'
    
    def __repr__(self):
        return f'Persona("{self.nombre}", {self.edad})'

# Crear instancia y probar métodos
p = Persona('Ana', 25)
print(str(p))
print(repr(p))
print(p)  # Usa __str__ por defecto
Ana, 25 años
Persona("Ana", 25)
Ana, 25 años
Explicación: El método __init__ inicializa la instancia, __str__ proporciona una representación amigable y __repr__ una representación técnica que podría usarse para recrear el objeto.

🎯 Sobrecarga de Operadores Aritméticos

La sobrecarga de operadores permite que nuestras clases respondan a operadores como +, -, *, / de manera personalizada. Los métodos especiales correspondientes son __add__, __sub__, __mul__, __div__, etc. También existen versiones de asignación como __iadd__ para +=. Es importante implementar operadores de manera consistente e intuitiva. Los operadores deben devolver nuevas instancias cuando sea apropiado, manteniendo la inmutabilidad cuando sea deseable. La sobrecarga de operadores hace que el código sea más legible y natural, permitiendo que los objetos personalizados se comporten como tipos numéricos nativos.

Clase Vector con sobrecarga de operadores aritméticos Copiar
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __str__(self):
        return f'Vector({self.x}, {self.y})'
    
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)
    
    def __sub__(self, other):
        return Vector(self.x - other.x, self.y - other.y)
    
    def __mul__(self, scalar):
        return Vector(self.x * scalar, self.y * scalar)

# Usar operadores sobrecargados
v1 = Vector(2, 3)
v2 = Vector(1, 4)
print(v1 + v2)
print(v1 - v2)
print(v1 * 3)
Vector(3, 7)
Vector(1, -1)
Vector(6, 9)
Explicación: Los operadores +, - y * ahora funcionan intuitivamente con objetos Vector, creando nuevas instancias con las operaciones matemáticas apropiadas.

💡 Operadores de Comparación y Métodos de Contenedor

Los operadores de comparación (__eq__, __lt__, __le__, __gt__, __ge__, __ne__) permiten que los objetos se comparen usando ==, <, <=, >, >=, !=. Los métodos de contenedor como __len__, __getitem__, __setitem__, __contains__ hacen que los objetos se comporten como secuencias o diccionarios. __len__ se invoca con len(), __getitem__ con [], __contains__ con 'in'. Estos métodos son fundamentales para crear clases que se integren con las funciones built-in de Python y los protocolos del lenguaje. Una implementación correcta permite que nuestros objetos se usen con sorted(), max(), min(), y otras funciones estándar.

Clase Lista personalizada con métodos de contenedor Copiar
class MiLista:
    def __init__(self):
        self.items = []
    
    def __len__(self):
        return len(self.items)
    
    def __getitem__(self, index):
        return self.items[index]
    
    def __setitem__(self, index, value):
        self.items[index] = value
    
    def __contains__(self, item):
        return item in self.items
    
    def agregar(self, item):
        self.items.append(item)

# Usar métodos de contenedor
mi_lista = MiLista()
mi_lista.agregar('Python')
mi_lista.agregar('Java')
print(len(mi_lista))
print(mi_lista[0])
print('Python' in mi_lista)
2
Python
True
Explicación: La clase MiLista se comporta como una lista nativa, respondiendo a len(), indexación con [] y el operador 'in' para verificar membresía.

🔧 Métodos Especiales Avanzados y Context Managers

Los métodos especiales avanzados incluyen __call__ (hace que el objeto sea callable), __enter__ y __exit__ (para context managers con 'with'), __iter__ y __next__ (para iteradores), y __hash__ (para objetos hasheable). __call__ permite que una instancia se comporte como una función. Los context managers con __enter__ y __exit__ manejan recursos de manera segura. Los iteradores permiten usar objetos en bucles for. __hash__ es necesario para usar objetos como claves de diccionario. Estos métodos proporcionan funcionalidad avanzada y hacen que las clases sean más versátiles y pythónicas.

Clase con métodos especiales avanzados - objeto callable e iterable Copiar
class Contador:
    def __init__(self, inicio=0, fin=5):
        self.inicio = inicio
        self.fin = fin
        self.actual = inicio
    
    def __call__(self):
        return f'Contador de {self.inicio} a {self.fin}'
    
    def __iter__(self):
        self.actual = self.inicio
        return self
    
    def __next__(self):
        if self.actual < self.fin:
            self.actual += 1
            return self.actual - 1
        raise StopIteration

# Usar objeto callable e iterable
c = Contador(1, 4)
print(c())  # Llamar como función
for num in c:  # Iterar sobre el objeto
    print(f'Número: {num}')
Contador de 1 a 4
Número: 1
Número: 2
Número: 3
Explicación: La clase Contador es tanto callable (se puede llamar como función) como iterable (se puede usar en bucles for), demostrando la versatilidad de los métodos especiales.

Ejercicios Prácticos

Clase Fracción con Operadores Aritméticos
INTERMEDIO

Descripción:

Crea una clase Fraccion que represente fracciones matemáticas. Implementa __init__, __str__, __repr__, y los operadores +, -, *, / para realizar operaciones entre fracciones. Incluye un método para simplificar fracciones.

Pista: Usa el algoritmo de Euclides para calcular el máximo común divisor y simplificar las fracciones.
Lista Personalizada con Métodos de Comparación
INTERMEDIO

Descripción:

Desarrolla una clase ListaOrdenada que mantenga sus elementos siempre ordenados. Implementa métodos de contenedor (__len__, __getitem__, __contains__) y comparación (__eq__, __lt__) para comparar listas por su longitud.

Pista: Usa bisect para mantener la lista ordenada al insertar elementos. Implementa __eq__ comparando elementos y __lt__ comparando longitudes.
Context Manager para Archivos
AVANZADO

Descripción:

Crea una clase MiArchivo que actúe como context manager para manejo de archivos. Implementa __enter__ y __exit__ para abrir y cerrar archivos automáticamente. Añade métodos para lectura y escritura.

Pista: En __enter__ abre el archivo y devuelve self. En __exit__ cierra el archivo y maneja posibles excepciones.
Clase Dinero con Múltiples Operadores
AVANZADO

Descripción:

Desarrolla una clase Dinero que maneje cantidades monetarias con diferentes monedas. Implementa operadores aritméticos, comparación, y métodos especiales para conversión de tipos. Incluye validación de monedas compatibles.

Pista: Almacena la cantidad y moneda. Solo permite operaciones entre la misma moneda. Implementa __float__ e __int__ para conversiones.

Resumen y Próximos Pasos

Los métodos especiales son fundamentales para crear clases pythónicas que se integren naturalmente con el ecosistema del lenguaje. Hemos explorado desde los métodos básicos como __init__, __str__ y __repr__, hasta conceptos avanzados como context managers e iteradores. La sobrecarga de operadores permite que nuestros objetos se comporten de manera intuitiva, mientras que los métodos de contenedor hacen que las clases personalizadas funcionen con las funciones built-in de Python. El siguiente tema explorará herencia y polimorfismo, conceptos que se complementan perfectamente con los métodos especiales para crear jerarquías de clases robustas y flexibles.

🏷️ Etiquetas:
python programación métodos especiales sobrecarga operadores POO