from __future__ import annotations
from typing import List, Optional, Union
from math import nan
import json
class Calificacion:
"""Representa una calificación con metadatos (extensible)."""
def __init__(self, valor: float, asignatura: Optional[str] = None, fecha: Optional[str] = None):
if not (0 <= valor <= 10):
raise ValueError("La calificación debe estar entre 0 y 10.")
if not isinstance(valor, (int, float)):
raise TypeError("La calificación debe ser numérica.")
self.valor = float(valor)
self.asignatura = asignatura
self.fecha = fecha
def __repr__(self):
return f"Calificacion(valor={self.valor}, asignatura={self.asignatura})"
class Estudiante:
"""Representa a un estudiante con nombre, edad y calificaciones estructuradas."""
def __init__(self, nombre: str, edad: int):
if not isinstance(nombre, str) or not nombre.strip():
raise ValueError("El nombre debe ser una cadena no vacía.")
if not isinstance(edad, int) or edad < 0:
raise ValueError("La edad debe ser un entero no negativo.")
self.nombre = nombre.strip()
self.edad = edad
self._calificaciones: List[Calificacion] = []
def agregar_calificacion(
self,
valor: Union[int, float],
asignatura: Optional[str] = None,
fecha: Optional[str] = None
) -> None:
"""Agrega una calificación con metadatos opcionales."""
calif = Calificacion(valor, asignatura, fecha)
self._calificaciones.append(calif)
def agregar_calificaciones(self, notas: List[Union[int, float]]) -> None:
"""Agrega múltiples calificaciones sin metadatos (modo rápido)."""
for nota in notas:
self.agregar_calificacion(nota)
@property
def promedio(self) -> float:
"""Devuelve el promedio aritmético. Si no hay calificaciones, devuelve NaN."""
if not self._calificaciones:
return nan
return sum(c.valor for c in self._calificaciones) / len(self._calificaciones)
@property
def calificaciones_valores(self) -> List[float]:
"""Devuelve solo los valores numéricos de las calificaciones."""
return [c.valor for c in self._calificaciones]
def estadisticas_descriptivas(self) -> dict:
"""Devuelve estadísticas básicas: promedio, mediana, desviación estándar."""
from statistics import mean, median, stdev
valores = self.calificaciones_valores
if not valores:
return {"promedio": nan, "mediana": nan, "desviacion_estandar": nan}
return {
"promedio": mean(valores),
"mediana": median(valores),
"desviacion_estandar": stdev(valores) if len(valores) > 1 else 0.0
}
def to_dict(self) -> dict:
"""Serializa el estudiante a un diccionario (compatible con JSON)."""
return {
"nombre": self.nombre,
"edad": self.edad,
"calificaciones": [
{
"valor": c.valor,
"asignatura": c.asignatura,
"fecha": c.fecha
}
for c in self._calificaciones
],
"promedio": self.promedio # nan no es JSON serializable; manejar en capa superior
}
def __repr__(self):
return f"Estudiante(nombre='{self.nombre}', edad={self.edad}, n_calif={len(self._calificaciones)})"
def __str__(self):
return f"{self.nombre} ({self.edad} años) - Promedio: {self.promedio:.2f} (n={len(self._calificaciones)})"
Last active
November 12, 2025 12:53
-
-
Save robintux/c8eb73efd51b9d6c10d9233884843cea to your computer and use it in GitHub Desktop.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment