Repaso: calculadora de formas
Parte de la sección Programación orientada a objetos del Journey de Python de Coddy. Lección 27 de 64.
Desafío
IntermedioEn este desafío, implementarás un sistema de cálculo de formas que demuestra el polimorfismo.
Implementa las clases requeridas en los siguientes archivos (busca los comentarios TODO en cada archivo):
shape.py- Define la clase base abstractaShapecircle.py- Implementa la claseCirclerectangle.py- Implementa la claseRectangletriangle.py- Implementa la claseTriangleunknownshape.py- Crea una clase que no derive de otra y que demuestre la tipificación patoshapecalculator.py- Implementa la funcionalidad de la calculadora
Sigue los comentarios TODO de cada archivo para obtener instrucciones paso a paso. El conjunto completo de pruebas te ayudará a comprender el comportamiento esperado y a asegurarte de que tu implementación cumpla correctamente todos los requisitos.
Pruébalo tú mismo
from circle import Circle
from rectangle import Rectangle
from triangle import Triangle
from unknownshape import UnknownShape
from shapecalculator import ShapeCalculator
import math
# Gestor de casos de prueba
test_case = input()
def test_basic_functionality():
calculator = ShapeCalculator()
# Prueba con Circle
circle = Circle(5)
print(f"Testing {circle}")
results = calculator.process_shape(circle)
print(f"Results: {results}")
print()
# Prueba con Rectangle
rectangle = Rectangle(4, 6)
print(f"Testing {rectangle}")
results = calculator.process_shape(rectangle)
print(f"Results: {results}")
print()
# Prueba con Triangle
triangle = Triangle(3, 4, 5)
print(f"Testing {triangle}")
results = calculator.process_shape(triangle)
print(f"Results: {results}")
print()
# Prueba con UnknownShape (tipado por comportamiento)
unknown = UnknownShape("Custom", 10)
print(f"Testing {unknown}")
results = calculator.process_shape(unknown)
print(f"Results: {results}")
def test_edge_cases():
calculator = ShapeCalculator()
# Prueba con valores cero
print("Testing zero values:")
circle = Circle(0)
print(f"Circle(0) area: {circle.area()}, perimeter: {circle.perimeter()}")
rectangle = Rectangle(0, 5)
print(f"Rectangle(0, 5) area: {rectangle.area()}, perimeter: {rectangle.perimeter()}")
# Prueba con valores muy grandes
print("\
Testing large values:")
large_circle = Circle(1000000)
print(f"Circle(1000000) area: {large_circle.area():.2e}, perimeter: {large_circle.perimeter():.2e}")
# Prueba con valores decimales
print("\
Testing decimal values:")
decimal_circle = Circle(0.5)
print(f"Circle(0.5) area: {decimal_circle.area()}, perimeter: {decimal_circle.perimeter()}")
decimal_triangle = Triangle(2.5, 3.5, 4.5)
print(f"Triangle(2.5, 3.5, 4.5) area: {decimal_triangle.area()}, perimeter: {decimal_triangle.perimeter()}")
def test_inheritance():
# Crea instancias de cada figura
circle = Circle(5)
rectangle = Rectangle(4, 6)
triangle = Triangle(3, 4, 5)
unknown = UnknownShape("Custom", 10)
# Prueba las relaciones de herencia
from shape import Shape
print(f"Circle is a Shape: {isinstance(circle, Shape)}")
print(f"Rectangle is a Shape: {isinstance(rectangle, Shape)}")
print(f"Triangle is a Shape: {isinstance(triangle, Shape)}")
print(f"UnknownShape is a Shape: {isinstance(unknown, Shape)}")
# Comprueba la disponibilidad de los métodos
print("\
Method availability:")
for shape_name, shape_obj in [("Circle", circle), ("Rectangle", rectangle),
("Triangle", triangle), ("UnknownShape", unknown)]:
print(f"{shape_name} has area(): {hasattr(shape_obj, 'area')}")
print(f"{shape_name} has perimeter(): {hasattr(shape_obj, 'perimeter')}")
print(f"{shape_name} has describe(): {hasattr(shape_obj, 'describe')}")
def test_polymorphism():
calculator = ShapeCalculator()
# Crea una lista de diferentes figuras
shapes = [
Circle(5),
Rectangle(4, 6),
Triangle(3, 4, 5),
UnknownShape("Custom", 10)
]
print("Testing polymorphic behavior:")
for i, shape in enumerate(shapes, 1):
print(f"\
Shape {i}: {shape.__class__.__name__}")
results = calculator.process_shape(shape)
print(f"Results: {results}")
def test_duck_typing():
calculator = ShapeCalculator()
# Crea una clase completamente nueva que tenga los métodos requeridos
class CustomDuckShape:
def __init__(self, name, factor):
self.name = name
self.factor = factor
def area(self):
return self.factor * 3
def perimeter(self):
return self.factor * 12
def describe(self):
return f"This is a {self.name} duck-typed shape with area {self.area()} and perimeter {self.perimeter()}"
def __str__(self):
return f"Custom {self.name} with factor {self.factor}"
# Prueba con la figura personalizada que usa tipado de pato
duck_shape = CustomDuckShape("DuckTyped", 5)
print(f"Testing duck typing with: {duck_shape}")
results = calculator.process_shape(duck_shape)
print(f"Results: {results}")
# Compara con una figura normal
circle = Circle(5)
print(f"\
Comparing with regular shape: {circle}")
results = calculator.process_shape(circle)
print(f"Results: {results}")
def test_invalid_inputs():
calculator = ShapeCalculator()
try:
# Prueba con un radio negativo
print("Testing Circle with negative radius:")
negative_circle = Circle(-5)
results = calculator.process_shape(negative_circle)
print(f"Results: {results}")
except Exception as e:
print(f"Error with negative circle: {e}")
try:
# Prueba con un triángulo no válido (lados que no pueden formar un triángulo)
print("\
Testing invalid Triangle (1, 1, 10):")
invalid_triangle = Triangle(1, 1, 10) # Incumple la desigualdad triangular
results = calculator.process_shape(invalid_triangle)
print(f"Results: {results}")
# Comprueba si el cálculo del área da un resultado válido
area = invalid_triangle.area()
if math.isnan(area) or not isinstance(area, float) or area <= 0:
print(f"Invalid triangle area: {area}")
except Exception as e:
print(f"Error with invalid triangle: {e}")
# Ejecuta la prueba correspondiente según la entrada
if test_case == "basic_functionality":
test_basic_functionality()
elif test_case == "edge_cases":
test_edge_cases()
elif test_case == "inheritance":
test_inheritance()
elif test_case == "polymorphism":
test_polymorphism()
elif test_case == "duck_typing":
test_duck_typing()
elif test_case == "invalid_inputs":
test_invalid_inputs()
else:
# Caso de prueba predeterminado
test_basic_functionality()Todas las lecciones de Programación orientada a objetos
1Fundamentos de la POO
Introducción a la POOArchivos externosClases vs. objetosEl parámetro selfMétodosAtributosMétodo constructor (__init__)Repaso: calculadora sencilla4Herencia
Herencia básicaLa función super()Sobrescritura de métodosHerencia múltipleOrden de resolución de métodosRepaso: 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 de propiedadDecorador de método estáticoDecorador de método de clase5Polimorfismo
Repaso de la sobrescritura de métodosTipado patoClases y métodos abstractosDiseño de interfacesRepaso: calculadora de formas8Conceptos avanzados de POO
Composición frente a 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. variables de claseDecoradores de propiedadesAtributos privadosRepaso: gestor de cuentas bancariasPractica por tu cuenta: Compilador de Python online