Dominando la Sobrecarga de Operadores en Python: Guía Práctica
Sobrecarga de Operadores en Python
Descubre cómo redefinir el comportamiento de operadores en Python usando métodos especiales como __add__, __lt__ y más, mejorando tus diseños OOP.
Sobrecarga de Operadores en Python
Los operadores integrados de Python funcionan diferente según los tipos de operandos. El operador +, por ejemplo, suma números, concatena cadenas o fusiona listas. Esta flexibilidad se logra mediante sobrecarga de operadores —una característica que permite asignar significados contextuales a operadores para tus propias clases.
Considera una clase simple Punto que representa una coordenada en un plano 2-D. Sin métodos especiales, intentar sumar dos puntos lanzará TypeError:
class Punto:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
p1 = Punto(1, 2)
p2 = Punto(2, 3)
print(p1+p2)
Salida
Traceback (most recent call last): File "<string>", line 9, in <module> print(p1+p2) TypeError: unsupported operand type(s) for +: 'Punto' and 'Punto'
Como Python no tiene conocimiento integrado de cómo combinar dos instancias Punto, lanza una excepción. La sobrecarga de operadores nos permite definir ese comportamiento nosotros mismos.
Métodos Especiales de Python
Métodos rodeados por doble guion bajo —__init__, __str__, __add__, etc.— son conocidos como métodos especiales o métodos "dunder". Se invocan implícitamente por el runtime de Python y son los puntos de enganche para sobrecarga de operadores, protocolos de contenedores y más.
Por ejemplo, el método __str__ controla cómo un objeto se convierte a cadena legible. Por defecto, imprimir un objeto muestra su dirección de memoria, lo que a menudo no es útil. Sobrescribir __str__ puede hacer depuración y logging mucho más claros:
class Punto:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return '({0}, {1})'.format(self.x, self.y)
Ahora, al imprimir un Punto, obtenemos:
p1 = Punto(2, 3)
print(p1)
Salida
(2, 3)
La misma representación se usa por str(p1) y format(p1) porque Python internamente llama a p1.__str__().
Sobrecargando el Operador +
Para personalizar la adición para objetos Punto, implementa __add__:
class Punto:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return '({0}, {1})'.format(self.x, self.y)
def __add__(self, other):
return Punto(self.x + other.x, self.y + other.y)
Probando el nuevo comportamiento:
p1 = Punto(1, 2)
p2 = Punto(2, 3)
print(p1 + p2)
Salida
(3, 5)
Bajo el capó, Python ejecuta p1.__add__(p2), que devuelve un nuevo Punto con las coordenadas sumadas.
Referencia rápida para otros operadores aritméticos y los métodos especiales a implementar:
| Operador | Expresión | Método Especial |
|---|---|---|
| Adición | p1 + p2 | __add__(self, other) |
| Sustracción | p1 - p2 | __sub__(self, other) |
| Multiplicación | p1 * p2 | __mul__(self, other) |
| Potencia | p1 ** p2 | __pow__(self, other) |
| División | p1 / p2 | __truediv__(self, other) |
| División Entera | p1 // p2 | __floordiv__(self, other) |
| Módulo | p1 % p2 | __mod__(self, other) |
| Desplazamiento Izquierda | p1 << p2 | __lshift__(self, other) |
| Desplazamiento Derecha | p1 >> p2 | __rshift__(self, other) |
| AND Bitwise | p1 & p2 | __and__(self, other) |
| OR Bitwise | p1 | p2 | __or__(self, other) |
| XOR Bitwise | p1 ^ p2 | __xor__(self, other) |
| NOT Bitwise | ~p1 | __invert__(self) |
Sobrecargando Operadores de Comparación
La sobrecarga de operadores no se limita a aritmética. Los operadores de comparación también pueden personalizarse. Por ejemplo, para comparar puntos basados en su distancia al origen, sobrescribe __lt__:
class Punto:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return '({0}, {1})'.format(self.x, self.y)
def __lt__(self, other):
self_mag = self.x**2 + self.y**2
other_mag = other.x**2 + other.y**2
return self_mag < other_mag
Uso:
p1 = Punto(1, 1)
p2 = Punto(-2, -3)
p3 = Punto(1, -1)
print(p1 < p2) # True
print(p2 < p3) # False
print(p1 < p3) # False
Otros métodos de comparación siguen un patrón similar:
| Operador | Expresión | Método Especial |
|---|---|---|
| Menor Que | p1 < p2 | __lt__(self, other) |
| Menor o Igual | p1 <= p2 | __le__(self, other) |
| Igual | p1 == p2 | __eq__(self, other) |
| Distinto | p1 != p2 | __ne__(self, other) |
| Mayor Que | p1 > p2 | __gt__(self, other) |
| Mayor o Igual | p1 >= p2 | __ge__(self, other) |
Definiendo cuidadosamente estos métodos, puedes hacer que objetos personalizados participen naturalmente en contextos aritméticos y de comparación, mejorando legibilidad y mantenibilidad del código.
python
- Dominando tipos de datos en Python: Guía práctica
- Dominando operadores en Python: Guía completa
- Dominando la sentencia pass en Python: Guía práctica
- Dominando argumentos de funciones en Python: Posicionales, por palabra clave y por defecto
- Dominando Diccionarios en Python: Creación, Manipulación y Técnicas Avanzadas
- Dominando la Herencia en Python: Conceptos, Sintaxis y Ejemplos Prácticos
- Dominando Iteradores en Python: Cómo Funcionan y Cómo Crear los Tuyos
- Explicación de Closures en Python: Cómo las Funciones Anidadas Capturan Variables
- Dominando el Decorador @property de Python: Getters y Setters Limpios y Compatibles
- Dominando datetime en Python: Guía Práctica de Fechas, Horas y Zonas Horarias
- Dominar time.sleep() en Python: Mejores Prácticas, Multihilo y Ejemplos Reales