Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
258 changes: 254 additions & 4 deletions lab-python-error-handling.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,265 @@
"execution_count": null,
"id": "cc2c441d-9dcf-4817-b097-cf6cbe440846",
"metadata": {},
"outputs": [],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"=== Inicialización del inventario ===\n",
"\n",
"=== Pedidos del cliente ===\n",
"Error: Producto inválido. Elige entre: t-shirt, mug, hat, book, keychain.\n",
"Error: Producto inválido. Elige entre: t-shirt, mug, hat, book, keychain.\n",
"Error: Producto inválido. Elige entre: t-shirt, mug, hat, book, keychain.\n",
"Error: Producto inválido. Elige entre: t-shirt, mug, hat, book, keychain.\n",
"Error: Producto inválido. Elige entre: t-shirt, mug, hat, book, keychain.\n",
"Error: Producto inválido. Elige entre: t-shirt, mug, hat, book, keychain.\n",
"Error: Producto inválido. Elige entre: t-shirt, mug, hat, book, keychain.\n",
"Error: Producto inválido. Elige entre: t-shirt, mug, hat, book, keychain.\n",
"Error: Producto inválido. Elige entre: t-shirt, mug, hat, book, keychain.\n",
"Error: Producto inválido. Elige entre: t-shirt, mug, hat, book, keychain.\n",
"Error: Producto inválido. Elige entre: t-shirt, mug, hat, book, keychain.\n",
"Error: Producto inválido. Elige entre: t-shirt, mug, hat, book, keychain.\n",
"Error: Producto inválido. Elige entre: t-shirt, mug, hat, book, keychain.\n",
"Error: Producto inválido. Elige entre: t-shirt, mug, hat, book, keychain.\n",
"Error: Producto inválido. Elige entre: t-shirt, mug, hat, book, keychain.\n"
]
}
],
"source": [
"# your code goes here"
"import re\n",
"import string\n",
"from typing import Iterable, List, Dict, Tuple, Callable\n",
"\n",
"# ==========================\n",
"# Utilidades y validaciones\n",
"# ==========================\n",
"\n",
"class InputTypeError(TypeError):\n",
" pass\n",
"\n",
"class MathDomainError(ValueError):\n",
" pass\n",
"\n",
"def assert_iterable_not_string(value, name=\"value\"):\n",
" if isinstance(value, (str, bytes)):\n",
" raise InputTypeError(f\"'{name}' no puede ser str/bytes; se esperaba una colección de elementos.\")\n",
" try:\n",
" iter(value)\n",
" except TypeError:\n",
" raise InputTypeError(f\"'{name}' debe ser iterable (lista, tupla, set, etc.).\")\n",
"\n",
"# =========================================\n",
"# 1) get_unique_list: únicos preservando orden\n",
"# =========================================\n",
"def get_unique_list(items: Iterable) -> List:\n",
" \"\"\"\n",
" Devuelve los elementos únicos preservando el orden de aparición.\n",
" Maneja entradas no iterables / strings con raise y mensajes claros.\n",
" \"\"\"\n",
" try:\n",
" assert_iterable_not_string(items, \"items\")\n",
" vistos = set()\n",
" resultado = []\n",
" for x in items:\n",
" if x not in vistos:\n",
" resultado.append(x)\n",
" vistos.add(x)\n",
" except InputTypeError as e:\n",
" print(f\"Error: {e}\")\n",
" raise\n",
" else:\n",
" return resultado\n",
" finally:\n",
" # Punto de trazabilidad (opcional)\n",
" pass\n",
"\n",
"# =========================================\n",
"# 2) count_case: conteo de mayúsculas/minúsculas\n",
"# =========================================\n",
"def count_case(texto: str) -> Dict[str, int]:\n",
" \"\"\"\n",
" Cuenta letras mayúsculas y minúsculas. Ignora no letras.\n",
" \"\"\"\n",
" if not isinstance(texto, str):\n",
" raise InputTypeError(\"count_case espera un str como entrada.\")\n",
" try:\n",
" upper = sum(1 for c in texto if c.isalpha() and c.isupper())\n",
" lower = sum(1 for c in texto if c.isalpha() and c.islower())\n",
" except Exception as e:\n",
" print(f\"Error inesperado en count_case: {e}\")\n",
" raise\n",
" else:\n",
" return {\"upper\": upper, \"lower\": lower}\n",
" finally:\n",
" pass\n",
"\n",
"# =========================================\n",
"# 3) remove_punctuation: elimina puntuación\n",
"# =========================================\n",
"_PUNCT_TABLE = str.maketrans(\"\", \"\", string.punctuation)\n",
"\n",
"def remove_punctuation(texto: str, keep: str = \"\") -> str:\n",
" \"\"\"\n",
" Elimina puntuación estándar. Si 'keep' incluye caracteres, estos se conservan.\n",
" \"\"\"\n",
" if not isinstance(texto, str):\n",
" raise InputTypeError(\"remove_punctuation espera un str.\")\n",
" try:\n",
" if keep:\n",
" # Construir una clase de regex que quite toda puntuación excepto los 'keep'\n",
" keep_escaped = re.escape(keep)\n",
" pattern = rf\"[{re.escape(string.punctuation).replace(keep_escaped, '')}]\"\n",
" return re.sub(pattern, \"\", texto)\n",
" # vía tabla rápida\n",
" return texto.translate(_PUNCT_TABLE)\n",
" except Exception as e:\n",
" print(f\"Error en remove_punctuation: {e}\")\n",
" raise\n",
" finally:\n",
" pass\n",
"\n",
"# =========================================\n",
"# 4) word_count: cuenta palabras (normalizado)\n",
"# =========================================\n",
"def word_count(texto: str, lowercase: bool = True, remove_punct: bool = True) -> Dict[str, int]:\n",
" \"\"\"\n",
" Cuenta frecuencia de palabras. Normaliza espacios; opcionalmente quita puntuación y pasa a minúsculas.\n",
" \"\"\"\n",
" if not isinstance(texto, str):\n",
" raise InputTypeError(\"word_count espera un str.\")\n",
" try:\n",
" if remove_punct:\n",
" texto = remove_punctuation(texto)\n",
" if lowercase:\n",
" texto = texto.lower()\n",
" # separa por cualquier espacio\n",
" palabras = re.findall(r\"\\S+\", texto)\n",
" freqs: Dict[str, int] = {}\n",
" for w in palabras:\n",
" freqs[w] = freqs.get(w, 0) + 1\n",
" except Exception as e:\n",
" print(f\"Error en word_count: {e}\")\n",
" raise\n",
" else:\n",
" return freqs\n",
" finally:\n",
" pass\n",
"\n",
"# =========================================\n",
"# 5) Calculadora con operaciones múltiples y recursividad\n",
"# =========================================\n",
"\n",
"def factorial(n: int) -> int:\n",
" \"\"\"Recursivo: factorial con validación.\"\"\"\n",
" if n < 0:\n",
" raise MathDomainError(\"factorial no definido para números negativos.\")\n",
" if n in (0, 1):\n",
" return 1\n",
" return n * factorial(n - 1)\n",
"\n",
"def potencia(base: float, exp: int) -> float:\n",
" \"\"\"Recursivo (exponente entero).\"\"\"\n",
" if exp == 0:\n",
" return 1.0\n",
" if exp < 0:\n",
" # a^(−n) = 1/a^n\n",
" return 1.0 / potencia(base, -exp)\n",
" # exp > 0\n",
" return base * potencia(base, exp - 1)\n",
"\n",
"def mcd(a: int, b: int) -> int:\n",
" \"\"\"Recursivo: máximo común divisor (Euclides).\"\"\"\n",
" a, b = abs(a), abs(b)\n",
" if b == 0:\n",
" return a\n",
" return mcd(b, a % b)\n",
"\n",
"Operation = Callable[..., float]\n",
"\n",
"def safe_div(a: float, b: float) -> float:\n",
" if b == 0:\n",
" raise MathDomainError(\"División por cero.\")\n",
" return a / b\n",
"\n",
"OPERACIONES: Dict[str, Callable] = {\n",
" \"sum\": lambda a, b: a + b,\n",
" \"sub\": lambda a, b: a - b,\n",
" \"mul\": lambda a, b: a * b,\n",
" \"div\": safe_div,\n",
" \"pow\": potencia, # exp entero\n",
" \"fact\": factorial, # unario\n",
" \"gcd\": mcd, # entero\n",
"}\n",
"\n",
"def calcular(op: str, *args) -> float:\n",
" \"\"\"\n",
" Enrutador de operaciones con manejo de errores y else/finally.\n",
" Uso:\n",
" calcular('sum', 2, 3) -> 5\n",
" calcular('div', 10, 2) -> 5\n",
" calcular('pow', 2, 8) -> 256\n",
" calcular('fact', 5) -> 120\n",
" calcular('gcd', 48, 18) -> 6\n",
" \"\"\"\n",
" try:\n",
" if op not in OPERACIONES:\n",
" raise KeyError(f\"Operación desconocida '{op}'. Disponibles: {', '.join(OPERACIONES)}\")\n",
" fn = OPERACIONES[op]\n",
"\n",
" # Validaciones según aridad conocida\n",
" if fn is factorial:\n",
" if len(args) != 1 or not isinstance(args[0], int):\n",
" raise InputTypeError(\"factorial espera 1 entero.\")\n",
" elif fn is mcd:\n",
" if len(args) != 2 or not all(isinstance(x, int) for x in args):\n",
" raise InputTypeError(\"gcd espera 2 enteros.\")\n",
" elif fn is potencia:\n",
" if len(args) != 2 or not isinstance(args[1], int):\n",
" raise InputTypeError(\"pow espera (base: float|int, exp: int).\")\n",
" else:\n",
" if len(args) != 2:\n",
" raise InputTypeError(f\"'{op}' espera 2 argumentos numéricos.\")\n",
"\n",
" res = fn(*args)\n",
" except (InputTypeError, MathDomainError, KeyError, TypeError, ValueError) as e:\n",
" print(f\"Error en calcular: {e}\")\n",
" raise\n",
" else:\n",
" return float(res) if isinstance(res, (int, float)) else res\n",
" finally:\n",
" # lugar para logs/telemetría/cierre de recursos si hiciera falta\n",
" pass\n",
"\n",
"# ==========================\n",
"# Mini tests / demostración\n",
"# ==========================\n",
"if __name__ == \"__main__\":\n",
" # 1) get_unique_list\n",
" print(get_unique_list([1, 2, 2, 3, 1, 4])) # [1, 2, 3, 4]\n",
"\n",
" # 2) count_case\n",
" print(count_case(\"Hola MUndo!!\")) # {'upper': 3, 'lower': 7}\n",
"\n",
" # 3) remove_punctuation\n",
" print(remove_punctuation(\"Hola, mundo! ¿Todo bien?\")) # \"Hola mundo ¿Todo bien\" (sin ! ,)\n",
"\n",
" # 4) word_count\n",
" print(word_count(\"Hola hola, mundo Mundo!\")) # {'hola': 2, 'mundo': 2}\n",
"\n",
" # 5) Calculadora\n",
" print(calcular(\"sum\", 2, 5)) # 7.0\n",
" print(calcular(\"div\", 10, 2)) # 5.0\n",
" print(calcular(\"pow\", 2, 8)) # 256.0\n",
" print(calcular(\"fact\", 5)) # 120.0\n",
" print(calcular(\"gcd\", 48, 18)) # 6.0\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"display_name": "venv",
"language": "python",
"name": "python3"
},
Expand All @@ -66,7 +316,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.13"
"version": "3.12.2"
}
},
"nbformat": 4,
Expand Down