Sobrecarga de operadores
Parte de la sección Object Oriented Programming del Journey de Python de Coddy — lección 34 de 64.
La sobrecarga de operadores permite que sus clases trabajen con los operadores integrados de Python (+, -, *, etc.) mediante la implementación de métodos mágicos especiales.
Aquí hay un ejemplo de una clase con sobrecarga de operadores:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
def __str__(self):
return f"Vector({self.x}, {self.y})"El método __add__ define qué sucede cuando usas el operador +:
v1 = Vector(2, 3)
v2 = Vector(5, 7)
result = v1 + v2 # Llama a v1.__add__(v2)
print(result)El método __mul__ define qué sucede cuando usas el operador *:
v1 = Vector(2, 3)
scaled = v1 * 3 # Llama a v1.__mul__(3)
print(scaled)Salida:
Vector(7, 10)
Vector(6, 9)Añade operadores de comparación:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __str__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(1, 1)
print(v1 == v2) # True - llama a v1.__eq__(v2)
print(v1 == v3) # FalsePunto clave: La sobrecarga de operadores utiliza métodos mágicos como __add__ (+), __sub__ (-), __mul__ (*), __eq__ (==) para definir cómo funcionan los operadores con tus objetos. Esto hace que tus clases se comporten de forma natural con los operadores integrados de Python.
Desafío
IntermedioEn este desafío, implementarás una clase Money que representa cantidades monetarias con una robusta sobrecarga de operadores. Tu implementación será probada exhaustivamente con una suite de pruebas completa.
money.py- Este es el único archivo que necesitas editar. Contiene la definición de la clase con comentarios TODO que guían tu implementación.driver.py- Contiene extensos escenarios de prueba que validan tu implementación (no lo modifiques).
Implementa la clase Money con las siguientes características:
- Constructor que recibe
amount(float) ycurrency(string) - Suma (
+) de objetos Money con la misma moneda - Multiplicación (
*) por un número para escalar la cantidad - Comparación de igualdad (
==) entre objetos Money - Representación de cadena en el formato “X.XX CUR”
Hoja de referencia
La sobrecarga de operadores permite que las clases trabajen con los operadores integrados de Python mediante la implementación de métodos mágicos especiales:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __str__(self):
return f"Vector({self.x}, {self.y})"Métodos mágicos comunes para la sobrecarga de operadores:
__add__para el operador+__sub__para el operador-__mul__para el operador*__eq__para el operador==__str__para la representación de cadena
Ejemplo de uso:
v1 = Vector(2, 3)
v2 = Vector(5, 7)
result = v1 + v2 # Llama a v1.__add__(v2)
scaled = v1 * 3 # Llama a v1.__mul__(3)
print(v1 == v2) # Llama a v1.__eq__(v2)Pruébalo tú mismo
from money import Money
# Manejador de casos de prueba
test_case = input()
def test_basic_functionality():
# Probar inicialización y representación de cadena
m1 = Money(10.0, "USD")
assert str(m1) == "10.00 USD", f"__str__ method failed, got {str(m1)}"
# Probar suma
m2 = Money(20.0, "USD")
m3 = m1 + m2
assert str(m3) == "30.00 USD", f"Addition failed, got {str(m3)}"
# Probar suma de diferentes monedas
m4 = Money(20.0, "EUR")
try:
m5 = m1 + m4
assert False, "Adding different currencies should raise an error"
except ValueError as e:
assert str(e) == "Cannot add different currencies", f"Wrong error message: {str(e)}"
# Probar multiplicación
m6 = m1 * 3
assert str(m6) == "30.00 USD", f"Multiplication failed, got {str(m6)}"
# Probar igualdad
assert m1 == Money(10.0, "USD"), "Equality test failed"
assert m1 != m2, "Inequality test failed"
print("All basic functionality tests passed!")
def test_zero_values():
# Probar con cantidad cero
m1 = Money(0.0, "USD")
assert str(m1) == "0.00 USD", f"Zero amount string representation failed, got {str(m1)}"
# Probar suma con cero
m2 = Money(10.0, "USD")
m3 = m1 + m2
assert str(m3) == "10.00 USD", f"Addition with zero failed, got {str(m3)}"
# Probar multiplicación por cero
m4 = m2 * 0
assert str(m4) == "0.00 USD", f"Multiplication by zero failed, got {str(m4)}"
# Probar igualdad con cantidad cero
assert m1 == Money(0.0, "USD"), "Equality with zero amount failed"
assert m1 != m2, "Inequality with zero amount failed"
print("All zero value tests passed!")
def test_negative_values():
# Probar con cantidad negativa
m1 = Money(-10.0, "USD")
assert str(m1) == "-10.00 USD", f"Negative amount string representation failed, got {str(m1)}"
# Probar suma con cantidades negativas
m2 = Money(20.0, "USD")
m3 = m1 + m2
assert str(m3) == "10.00 USD", f"Addition with negative amount failed, got {str(m3)}"
m4 = Money(-5.0, "USD")
m5 = m1 + m4
assert str(m5) == "-15.00 USD", f"Addition of two negative amounts failed, got {str(m5)}"
# Probar multiplicación por escalar negativo
m6 = m2 * -2
assert str(m6) == "-40.00 USD", f"Multiplication by negative scalar failed, got {str(m6)}"
# Probar igualdad con cantidades negativas
assert m1 == Money(-10.0, "USD"), "Equality with negative amount failed"
assert m1 != m2, "Inequality with negative amount failed"
print("All negative value tests passed!")
def test_large_values():
# Probar con cantidades muy grandes
m1 = Money(1000000.0, "USD")
assert str(m1) == "1000000.00 USD", f"Large amount string representation failed, got {str(m1)}"
# Probar suma con cantidades grandes
m2 = Money(2000000.0, "USD")
m3 = m1 + m2
assert str(m3) == "3000000.00 USD", f"Addition with large amounts failed, got {str(m3)}"
# Probar multiplicación con escalar grande
m4 = m1 * 1000
assert str(m4) == "1000000000.00 USD", f"Multiplication with large scalar failed, got {str(m4)}"
print("All large value tests passed!")
def test_precision():
# Probar con cantidades fraccionarias
m1 = Money(10.25, "USD")
assert str(m1) == "10.25 USD", f"Fractional amount string representation failed, got {str(m1)}"
m2 = Money(10.2, "USD")
assert str(m2) == "10.20 USD", f"Two decimal place formatting failed, got {str(m2)}"
# Probar suma con cantidades fraccionarias
m3 = Money(0.75, "USD")
m4 = m1 + m3
assert str(m4) == "11.00 USD", f"Addition with fractional amounts failed, got {str(m4)}"
# Probar multiplicación con escalar fraccionario
m5 = m1 * 0.5
assert str(m5) == "5.13 USD", f"Multiplication with fractional scalar failed, got {str(m5)}"
print("All precision tests passed!")
def test_type_validation():
try:
# Estas operaciones deberían funcionar sin errores
m1 = Money(10.0, "USD")
m2 = m1 * 2
m3 = m1 * 2.5
# Probar igualdad con diferentes tipos
assert (m1 == "10.00 USD") == False, "Equality with string should return False"
assert (m1 == 10.0) == False, "Equality with number should return False"
print("All type validation tests passed!")
except Exception as e:
print(f"Type validation test failed: {e}")
def test_currency_case_sensitivity():
# Probar sensibilidad a mayúsculas en la moneda
m1 = Money(10.0, "USD")
m2 = Money(10.0, "usd")
# Las monedas deben ser sensibles a mayúsculas
assert m1 != m2, "Currency comparison should be case-sensitive"
# Sumar diferentes casos debería fallar
try:
m3 = m1 + m2
assert False, "Adding different currency cases should raise an error"
except ValueError as e:
assert str(e) == "Cannot add different currencies", f"Wrong error message: {str(e)}"
print("All currency case sensitivity tests passed!")
def test_performance():
# Crear muchos objetos Money y realizar operaciones
base = Money(1.0, "USD")
result = base
# Realizar 1000 sumas
for i in range(1000):
result = result + Money(1.0, "USD")
assert str(result) == "1001.00 USD", f"Performance test addition failed, got {str(result)}"
# Realizar 10 multiplicaciones
result = base
for i in range(10):
result = result * 2
assert str(result) == "1024.00 USD", f"Performance test multiplication failed, got {str(result)}"
print("All performance tests passed!")
# Ejecutar la prueba apropiada basada en la entrada
if test_case == "basic_test":
test_basic_functionality()
elif test_case == "zero_values":
test_zero_values()
elif test_case == "negative_values":
test_negative_values()
elif test_case == "large_values":
test_large_values()
elif test_case == "precision":
test_precision()
elif test_case == "type_validation":
test_type_validation()
elif test_case == "currency_case":
test_currency_case_sensitivity()
elif test_case == "performance":
test_performance()
else:
print(f"Unknown test case: {test_case}")Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Object Oriented Programming
1Fundamentos de la POO
Archivos externosIntroducción a la POOClases vs ObjetosEl parámetro selfMétodosAtributosMétodo constructor (__init__)Resumen - Calculadora simple4Herencia
Herencia básicaLa función super()Sobrescritura de métodosHerencia múltipleOrden de resolución de métodosResumen - Jerarquía de empleados7Métodos especiales
Introducción a los métodos mágicosSobrecarga de operadoresMétodos mágicos de contenedoresRepaso - Lista personalizada10Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy2Decoradores
Introducción a los decoradoresDecorador propertyDecorador de métodos estáticosDecorador de métodos de clase5Polimorfismo
Sobreescritura de métodos revisitadaDuck TypingClases y métodos abstractosDiseño de interfacesResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. HerenciaMixinsMétodos estáticos y de claseDecoradores de claseGestores de contexto11Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite3Propiedades de clase
Variables de instancia vs de claseDecoradores de propiedadesAtributos privadosResumen - Gestor de cuentas bancarias