{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<p>\n",
    "<font size='5' face='Georgia, Arial'>IIC-2233 Apunte Programación Avanzada</font><br>\n",
    "<font size='1'>&copy; 2015 Karim Pichara - Christian Pieringer. Todos los derechos reservados.</font>\n",
    "<br>\n",
    "<font size='1'>Modificado en 2017-1 al 2026-1 por Equipo Docente IIC2233.</font>\n",
    "</p>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tabla de contenidos\n",
    "\n",
    "1. [1. Excepciones](#Excepciones)\n",
    "2. [Tipos de excepciones](#Tipos-de-excepciones)\n",
    "    1. [`SyntaxError`](#syntaxerror)\n",
    "    2. [`IndentationError`](#indentationerror)\n",
    "    3. [`NameError`](#nameerror)\n",
    "    4. [`ZeroDivisionError`](#zerodivisionerror)\n",
    "    5. [`IndexError`](#indexerror)\n",
    "    6. [`KeyError`](#keyerror)\n",
    "    7. [`AttributeError`](#attributeerror)\n",
    "    8. [`TypeError`](#typeerror)\n",
    "    9. [`ValueError`](#valueerror)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1. Excepciones"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En Ciencia de la Computación, una **excepción** es una condición anómala o inesperada que ocurre durante la ejecución de un programa y que interrumpe su **ejecución esperada**.\n",
    "Cuando esto sucede, los sistemas generan un evento denominado **excepción** que altera el curso habitual del programa.\n",
    "\n",
    "Entre las situaciones más comunes que pueden generar excepciones se encuentran:\n",
    "\n",
    "* Operaciones no definidas (ej. división por cero).\n",
    "* Acceso a regiones de memoria no permitidas (ej. leer más allá de los límites de una lista).\n",
    "* Acceder a una *key* inexistente en un diccionario.\n",
    "* Construir un número a partir de un *string* con formato incorrecto: `int(\"34C\")`.\n",
    "* Usar una variable no definida.\n",
    "* Invocar un método inexistente en un objeto.\n",
    "* Realizar una operación prohibida para un tipo de dato (ej. modificar una tupla).\n",
    "\n",
    "Aunque estos casos podrían manejarse con estructuras de control como `if`/`elif`/`else`, hacerlo así complica el código y lo vuelve más difícil de mantener. Además, cada nueva condición obligaría a modificar múltiples partes del programa, aumentando el riesgo de errores.\n",
    "\n",
    "Por ello, lenguajes como Python permiten **generar** (*raise*) excepciones cuando ocurre una situación anómala y **capturarlas** (*catch*) en bloques de código diseñados para tratarlas. Este proceso, llamado **manejo de excepciones** (*exception handling*), permite:\n",
    "\n",
    "* Corregir el problema.\n",
    "* Notificar al usuario.\n",
    "* Ignorarlo y continuar.\n",
    "* O tomar cualquier acción que permita retomar la ejecución normal del programa.\n",
    "\n",
    "Si una excepción no es manejada, se reporta al sistema operativo y, por lo general, el programa finaliza abruptamente. En Python, esto significa que el intérprete detendrá la ejecución y mostrará un mensaje indicando el tipo de error ocurrido.\n",
    "\n",
    "En Python, las excepciones son objetos que heredan de la clase Exception y se crean en el momento en que se detecta el problema. Algunas excepciones en Python 3.x pueden deberse a:\n",
    "\n",
    "* Error del usuario al ingresar algún dato.\n",
    "* Argumentos no válidos al llamar una función (cantidad de parámetros, tipo de datos, etc.).\n",
    "* Error de sintaxis, al intentar ejecutar código con sintaxis ambigua o incorrecta.\n",
    "* Intentar utilizar variables inexistentes (solo se genera cuando se accede a ellas).\n",
    "* Invocar un método inexistente dentro de un objeto.\n",
    "* Ejecutar una operación prohibida para un tipo de dato (por ejemplo, modificar un elemento de una tupla).\n",
    "\n",
    "Dado que Python es un lenguaje **interpretado**, estas **excepciones se producen en tiempo de ejecución (runtime exceptions)**. Esto implica que un error como el uso de una variable no definida no provocará una excepción a menos que el flujo del programa llegue a esa instrucción.\n",
    "\n",
    "En cambio, los lenguajes **compilados** (como Java, C o C#) son traducidos a código máquina antes de ejecutarse. Durante la compilación, pueden detectarse errores como variables no definidas, uso incorrecto de tipos o métodos inexistentes. Sin embargo, las excepciones propias de la ejecución seguirán ocurriendo únicamente al momento de correr el programa.\n",
    "\n",
    "Un **manejo adecuado de excepciones** es clave para desarrollar programas **robustos**, capaces de recuperarse ante errores inesperados o entradas inválidas. Después de todo, no nos gustaría que nuestro juego favorito se cayera por completo cada vez que nos equivocamos y escribimos una `O` en un lugar de un `0` (o una `l` en lugar de `1`)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tipos de excepciones"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cada lenguaje posee distintos tipos de excepciones. A continuación revisaremos algunos ejemplos de clases de excepciones comunes en Python. Todas ellas son clases que heredan de `Exception`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## SyntaxError\n",
    "\n",
    "Se genera cuando una sentencia del programa está mal escrita y viola sus reglas sintácticas. Esta excepción se levanta antes de comenzar a ejecutar el programa, se levanta al leer todo el código que se piensa ejecutar.\n",
    "\n",
    "Por ejemplo, el uso de la sentencia `print` sin paréntesis es válido en Python 2.x, pero incorrecto en Python3.x."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "Missing parentheses in call to 'print'. Did you mean print(...)? (796388850.py, line 1)",
     "output_type": "error",
     "traceback": [
      "  \u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[31m    \u001b[39m\u001b[31mprint \"Hello World\"\u001b[39m\n    ^\n\u001b[31mSyntaxError\u001b[39m\u001b[31m:\u001b[39m Missing parentheses in call to 'print'. Did you mean print(...)?\n"
     ]
    }
   ],
   "source": [
    "print \"Hello World\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En cambio, el uso de `print` válido en Python3.x. es como una función y requiere parámetros dentro de paréntesis `()`. En Python2.x `print` es una palabra clave reservada, pero no una función."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello World\n"
     ]
    }
   ],
   "source": [
    "print(\"Hello World\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cuando se nos olvida cerrar un paréntesis, un string, o escribir un `:` también es un error de sintaxis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "incomplete input (3841510335.py, line 1)",
     "output_type": "error",
     "traceback": [
      "  \u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[31m    \u001b[39m\u001b[31mprint(\"Uwu\"\u001b[39m\n               ^\n\u001b[31mSyntaxError\u001b[39m\u001b[31m:\u001b[39m incomplete input\n"
     ]
    }
   ],
   "source": [
    "print(\"Uwu\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "unterminated string literal (detected at line 1) (963869426.py, line 1)",
     "output_type": "error",
     "traceback": [
      "  \u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[5]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[31m    \u001b[39m\u001b[31m\"Uwu\u001b[39m\n    ^\n\u001b[31mSyntaxError\u001b[39m\u001b[31m:\u001b[39m unterminated string literal (detected at line 1)\n"
     ]
    }
   ],
   "source": [
    "\"Uwu"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "expected ':' (289892626.py, line 1)",
     "output_type": "error",
     "traceback": [
      "  \u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[31m    \u001b[39m\u001b[31mfor i in range(9)\u001b[39m\n                     ^\n\u001b[31mSyntaxError\u001b[39m\u001b[31m:\u001b[39m expected ':'\n"
     ]
    }
   ],
   "source": [
    "for i in range(9)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## IndentationError\n",
    "\n",
    "Este error, que hereda de `SyntaxError`, ocurre cuando una línea de código no respeta la indentación correcta.\n",
    "En Python, cada vez que se define un **bloque de código** o *scope* (por ejemplo, con `if`, `for`, `def`, etc.) es obligatorio incluir al menos una instrucción correctamente indentada dentro de él. En el siguiente ejemplo, se abre un bloque con `if`, pero la línea posterior no está indentada, lo que provoca la excepción."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "ename": "IndentationError",
     "evalue": "expected an indented block after 'if' statement on line 2 (1578159015.py, line 4)",
     "output_type": "error",
     "traceback": [
      "  \u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[28]\u001b[39m\u001b[32m, line 4\u001b[39m\n\u001b[31m    \u001b[39m\u001b[31mprint(\"Watch out! I am the Queen of Hearts from Wonderland\")\u001b[39m\n    ^\n\u001b[31mIndentationError\u001b[39m\u001b[31m:\u001b[39m expected an indented block after 'if' statement on line 2\n"
     ]
    }
   ],
   "source": [
    "user = \"Alice\"\n",
    "if user != \"Queen of Hearts\":\n",
    "    # Verifica si el user es \"Queen of Hearts\"\n",
    "print(\"Watch out! I am the Queen of Hearts from Wonderland\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "> Cabe destacar que un comentario no evita este error porque no es código ejecutable. En cambio, una instrucción como `pass` sí lo hace."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Watch out! I am the Queen of Hearts from Wonderland\n"
     ]
    }
   ],
   "source": [
    "user = \"Alice\"\n",
    "if user != \"Queen of Hearts\":\n",
    "    # Este comentario no sirve\n",
    "    pass  # El pass\n",
    "print(\"Watch out! I am the Queen of Hearts from Wonderland\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## NameError\n",
    "\n",
    "Este error ocurre cuando el programa intenta acceder a un nombre —ya sea de una variable, función o clase— que no ha sido definido previamente en el ámbito local o global. En otras palabras, el identificador es desconocido para el intérprete.\n",
    "\n",
    "Por ejemplo, en Python 2.x la función para leer datos del usuario es distinta a la de Python 3.x, por lo que usar la versión incorrecta del comando puede generar un `NameError`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'raw_input' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mNameError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[9]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m a = \u001b[43mraw_input\u001b[49m(\u001b[33m\"\u001b[39m\u001b[33mEnter a number: \u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m      2\u001b[39m a\n",
      "\u001b[31mNameError\u001b[39m: name 'raw_input' is not defined"
     ]
    }
   ],
   "source": [
    "a = raw_input(\"Enter a number: \")\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En Python3.x. la función `raw_input()` fue renombrada a `input()`, por lo tanto, ante una llamada este primer nombre no puede ser hallado por el intérprete de Python."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'raw_input' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mNameError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[11]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m a = \u001b[43mraw_input\u001b[49m(\u001b[33m\"\u001b[39m\u001b[33mEnter a number: \u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m      2\u001b[39m a\n",
      "\u001b[31mNameError\u001b[39m: name 'raw_input' is not defined"
     ]
    }
   ],
   "source": [
    "a = raw_input(\"Enter a number: \")\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cuando se intenta leer el valor de una variable que no ha sido previamente definida, también ocurre un error de nombre."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'c' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mNameError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[12]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m      1\u001b[39m a = \u001b[32m42\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m b = \u001b[43mc\u001b[49m + a\n",
      "\u001b[31mNameError\u001b[39m: name 'c' is not defined"
     ]
    }
   ],
   "source": [
    "a = 42\n",
    "b = c + a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Recordemos que las excepciones en Python solo se detectan en tiempo de ejecución (*runtime*), ya que es un lenguaje interpretado. Observa el siguiente código:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "84\n"
     ]
    }
   ],
   "source": [
    "a = 42\n",
    "if a < 42:\n",
    "    b = c + a\n",
    "else:\n",
    "    b = a * 2\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Este ejemplo contiene un error, al igual que el código anterior, ya que no se ha definido la variable `c`. Sin embargo, si esa instrucción nunca se ejecuta, la excepción no se genera. Si hubiésemos escrito un código similar en un lenguaje compilado, el proceso de compilación sí hubiese detectado este error (y lo hubiésemos corregido)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## ZeroDivisionError\n",
    "\n",
    "Esta excepción es lanzada cuando el segundo elemento, o denominador, de una división es cero.\n",
    "\n",
    "En el ejemplo vemos que la función `divide` está correctamente escrita, sin embargo, los valores ingresados por el usuario producen este error matemático."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.25\n"
     ]
    },
    {
     "ename": "ZeroDivisionError",
     "evalue": "division by zero",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mZeroDivisionError\u001b[39m                         Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[14]\u001b[39m\u001b[32m, line 8\u001b[39m\n\u001b[32m      6\u001b[39m w = divide(\u001b[32m5\u001b[39m, r)\n\u001b[32m      7\u001b[39m \u001b[38;5;28mprint\u001b[39m(w)\n\u001b[32m----> \u001b[39m\u001b[32m8\u001b[39m z = \u001b[43mdivide\u001b[49m\u001b[43m(\u001b[49m\u001b[32;43m5\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mr\u001b[49m\u001b[43m \u001b[49m\u001b[43m-\u001b[49m\u001b[43m \u001b[49m\u001b[32;43m4\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m      9\u001b[39m \u001b[38;5;28mprint\u001b[39m(z)\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[14]\u001b[39m\u001b[32m, line 2\u001b[39m, in \u001b[36mdivide\u001b[39m\u001b[34m(x, y)\u001b[39m\n\u001b[32m      1\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mdivide\u001b[39m(x, y):\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mx\u001b[49m\u001b[43m \u001b[49m\u001b[43m/\u001b[49m\u001b[43m \u001b[49m\u001b[43my\u001b[49m\n",
      "\u001b[31mZeroDivisionError\u001b[39m: division by zero"
     ]
    }
   ],
   "source": [
    "def divide(x, y):\n",
    "    return x / y\n",
    "\n",
    "\n",
    "r = 4\n",
    "w = divide(5, r)\n",
    "print(w)\n",
    "z = divide(5, r - 4)\n",
    "print(z)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En este caso, el problema no está en la definición de la función `divide` (aunque la excepción se genere ahí), sino en el **uso** que se hace de ella y en los argumentos que recibe. En el primer llamado, los parámetros son válidos; en el segundo, en cambio, se pasa un valor que provoca la excepción (¿podríamos haberlo previsto antes de llamar a `divide`?).\n",
    "\n",
    "Este tipo de error solo puede detectarse en **tiempo de ejecución**, ya que al leer el código de `divide` no es posible anticipar si alguna vez será llamada con un `y` igual a `0`.\n",
    "\n",
    "La función no es robusta, es decir, no está preparada para resistir este tipo de fallos. Más adelante, al aprender a detectar y manejar excepciones, podremos implementar mecanismos para corregirlo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## IndexError\n",
    "\n",
    "Este error se produce cuando se intenta acceder a un índice que está fuera del rango válido de una secuencia.\n",
    "\n",
    "El caso más común ocurre al referirse a un elemento de una lista, tupla u otra estructura indexable con un número de índice que excede el límite permitido por la cantidad de elementos que contiene. En Python, las secuencias como las listas se indexan desde `0` hasta `len(lista) - 1`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "ename": "IndexError",
     "evalue": "tuple index out of range",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mIndexError\u001b[39m                                Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[15]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m      1\u001b[39m age = (\u001b[32m36\u001b[39m, \u001b[32m23\u001b[39m, \u001b[32m12\u001b[39m)\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m \u001b[43mage\u001b[49m\u001b[43m[\u001b[49m\u001b[32;43m3\u001b[39;49m\u001b[43m]\u001b[49m\n",
      "\u001b[31mIndexError\u001b[39m: tuple index out of range"
     ]
    }
   ],
   "source": [
    "age = (36, 23, 12)\n",
    "age[3]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## KeyError\n",
    "\n",
    "Esta excepción alerta sobre el uso incorrecto o inválido de llaves (*keys*) en diccionarios y *mappings*, similarmente a `IndexError` en listas.\n",
    "\n",
    "En el ejemplo a continuación, el usuario pide un dato asociado a una llave inexistente en el diccionario. Al no existir, se levanta la excepción (aunque podríamos haber usado `defaultdict` y no toparnos con esta excepción)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "ename": "KeyError",
     "evalue": "'publisher'",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mKeyError\u001b[39m                                  Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[16]\u001b[39m\u001b[32m, line 4\u001b[39m\n\u001b[32m      1\u001b[39m books = {\u001b[33m\"\u001b[39m\u001b[33mauthor\u001b[39m\u001b[33m\"\u001b[39m: \u001b[33m\"\u001b[39m\u001b[33mJuanito Los Palotes\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m      2\u001b[39m          \u001b[33m\"\u001b[39m\u001b[33mpages\u001b[39m\u001b[33m\"\u001b[39m: \u001b[32m9877\u001b[39m}\n\u001b[32m----> \u001b[39m\u001b[32m4\u001b[39m \u001b[43mbooks\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mpublisher\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\n",
      "\u001b[31mKeyError\u001b[39m: 'publisher'"
     ]
    }
   ],
   "source": [
    "books = {\"author\": \"Juanito Los Palotes\",\n",
    "         \"pages\": 9877}\n",
    "\n",
    "books[\"publisher\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## AttributeError\n",
    "\n",
    "Esta excepción alerta sobre el uso incorrecto de métodos o atributos de una clase o tipo de dato.\n",
    "\n",
    "En este ejemplo, la clase `Car` ha definido el método avanzar. Sin embargo, el usuario ejecuta el método `stop()` que no existe en ella."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'Car' object has no attribute 'stop'",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mAttributeError\u001b[39m                            Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[17]\u001b[39m\u001b[32m, line 11\u001b[39m\n\u001b[32m      7\u001b[39m         \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mmoving forward\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m     10\u001b[39m chevy = Car()\n\u001b[32m---> \u001b[39m\u001b[32m11\u001b[39m \u001b[43mchevy\u001b[49m\u001b[43m.\u001b[49m\u001b[43mstop\u001b[49m()\n",
      "\u001b[31mAttributeError\u001b[39m: 'Car' object has no attribute 'stop'"
     ]
    }
   ],
   "source": [
    "class Car:\n",
    "\n",
    "    def __init__(self, doors=4):\n",
    "        self.doors = doors\n",
    "\n",
    "    def move_forward(self):\n",
    "        print(\"moving forward\")\n",
    "\n",
    "\n",
    "chevy = Car()\n",
    "chevy.stop()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## TypeError\n",
    "\n",
    "Esta excepción indica que hubo un manejo erróneo de **tipos** de datos. Es decir, cuando se intenta ejecutar una operación o función con un argumento que no pertenece al tipo correcto para la ejecución.\n",
    "\n",
    "Un error común es concatenar una lista con algo que no es del tipo `list`. En este ejemplo, la función está definida como la suma de variables, sin embargo, debido a *duck-typing*, el operador `+` se comporta distinto de acuerdo a los tipos de los datos que recibe."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "63\n",
      "[36, 23, 12, 40, 25]\n"
     ]
    },
    {
     "ename": "TypeError",
     "evalue": "unsupported operand type(s) for +: 'int' and 'list'",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mTypeError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[18]\u001b[39m\u001b[32m, line 10\u001b[39m\n\u001b[32m      8\u001b[39m \u001b[38;5;28mprint\u001b[39m(combine(ages[\u001b[32m1\u001b[39m], more_ages[\u001b[32m0\u001b[39m]))  \u001b[38;5;66;03m# 23 + 40 = 63\u001b[39;00m\n\u001b[32m      9\u001b[39m \u001b[38;5;28mprint\u001b[39m(combine(ages, more_ages))        \u001b[38;5;66;03m# concatenates the two lists\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m10\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[43mcombine\u001b[49m\u001b[43m(\u001b[49m\u001b[32;43m2\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mages\u001b[49m\u001b[43m)\u001b[49m)                \u001b[38;5;66;03m# TypeError: unsupported operand types for +: 'int' and 'list'\u001b[39;00m\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[18]\u001b[39m\u001b[32m, line 2\u001b[39m, in \u001b[36mcombine\u001b[39m\u001b[34m(x, y)\u001b[39m\n\u001b[32m      1\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mcombine\u001b[39m(x, y):\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mx\u001b[49m\u001b[43m \u001b[49m\u001b[43m+\u001b[49m\u001b[43m \u001b[49m\u001b[43my\u001b[49m\n",
      "\u001b[31mTypeError\u001b[39m: unsupported operand type(s) for +: 'int' and 'list'"
     ]
    }
   ],
   "source": [
    "def combine(x, y):\n",
    "    return x + y\n",
    "\n",
    "\n",
    "ages = [36, 23, 12]\n",
    "more_ages = [40, 25]\n",
    "\n",
    "print(combine(ages[1], more_ages[0]))  # 23 + 40 = 63\n",
    "print(combine(ages, more_ages))        # concatenates the two lists\n",
    "print(combine(2, ages))                # TypeError: unsupported operand types for +: 'int' and 'list'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Podemos observar que en el primer llamado el operador `+` recibió dos `int`, por lo tanto, se comportó como una suma aritmética. En el segundo llamado, el operador `+` recibió dos `list`, por lo tanto, se comportó como una concatenación de listas. Sin embargo, en el tercer caso recibió `int` y `list`, y no estaba definido qué hacer con ese tipo de operandos."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Otro error suele surgir cuando intentamos llamar un objeto que no tiene implementado el método `__call__`. Este método permite que el objeto pueda ser llamado cuando utilizamos los parentesis `()` (también nombrado como operador de llamada). Por ejemplo, esto ocurre cuando colocamos paréntesis después de un `int`, `str`, `list`, entre otros."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "'str' object is not callable",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mTypeError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[19]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m      1\u001b[39m name = \u001b[33m\"\u001b[39m\u001b[33mAnya\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m \u001b[43mname\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[31mTypeError\u001b[39m: 'str' object is not callable"
     ]
    }
   ],
   "source": [
    "name = \"Anya\"\n",
    "name()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "'int' object is not callable",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mTypeError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[20]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m      1\u001b[39m amount = \u001b[32m42\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m \u001b[43mamount\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[31mTypeError\u001b[39m: 'int' object is not callable"
     ]
    }
   ],
   "source": [
    "amount = 42\n",
    "amount()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "'list' object is not callable",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mTypeError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[21]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m      1\u001b[39m books = [\u001b[33m\"\u001b[39m\u001b[33mDon Quijote de la Mancha\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33mLos cuentos de Canterbury\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33mEl Principito\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33mEl retrato de Dorian Gray\u001b[39m\u001b[33m\"\u001b[39m]\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m \u001b[43mbooks\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[31mTypeError\u001b[39m: 'list' object is not callable"
     ]
    }
   ],
   "source": [
    "books = [\"Don Quijote de la Mancha\", \"Los cuentos de Canterbury\", \"El Principito\", \"El retrato de Dorian Gray\"]\n",
    "books()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## ValueError\n",
    "\n",
    "Esta excepción indica que hubo un manejo erróneo de **valor** de datos. Es decir, cuando se intenta ejecutar una operación o función con un argumento cuyo **valor no era apropiado** para la ejecución esperada.\n",
    "\n",
    "Podemos encontrar este tipo de error en múltiples funciones conocidas. Por ejemplo, los *strings* de Python tienen el método `split` que permite separar la cadena de texto según algún separador que recibe como argumento:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['My', 'string', 'separable', 'by', 'spaces']"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"My string separable by spaces\".split(\" \")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "empty separator",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mValueError\u001b[39m                                Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[23]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[33;43m\"\u001b[39;49m\u001b[33;43mMy string separable by spaces\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43msplit\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n",
      "\u001b[31mValueError\u001b[39m: empty separator"
     ]
    }
   ],
   "source": [
    "\"My string separable by spaces\".split(\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si a esta función le entregamos como argumento un *string* vacío, arroja `ValueError` debido a que necesita que este tenga contenido para lograr separar una palabra.\n",
    "\n",
    "Otro ejemplo es el método `remove` de listas de Python, este recibe un elemento que remover de la lista y de encontrarlo, lo hace:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 2, 4, 5]"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_list = [1, 2, 3, 4, 5]\n",
    "my_list.remove(3)\n",
    "my_list"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "list.remove(x): x not in list",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mValueError\u001b[39m                                Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[25]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m      1\u001b[39m my_list = [\u001b[32m1\u001b[39m, \u001b[32m2\u001b[39m, \u001b[32m3\u001b[39m, \u001b[32m4\u001b[39m, \u001b[32m5\u001b[39m]\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m \u001b[43mmy_list\u001b[49m\u001b[43m.\u001b[49m\u001b[43mremove\u001b[49m\u001b[43m(\u001b[49m\u001b[32;43m6\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m      3\u001b[39m my_list\n",
      "\u001b[31mValueError\u001b[39m: list.remove(x): x not in list"
     ]
    }
   ],
   "source": [
    "my_list = [1, 2, 3, 4, 5]\n",
    "my_list.remove(6)\n",
    "my_list"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En el último caso, si se le entrega un elemento que no existe en la lista, arroja `ValueError` debido a que necesita que el elemento esté en la lista para removerlo. A diferencia del ejemplo anterior, el error no era intrínseco del valor del argumento, sino que era específico a la lista que llamó el método. Al llamar `remove(6)` de una lista que si contiene un `6`, no hubiera arrojado un error. Para `remove` el valor no era apropiado para la instancia que llamaba al método, mientras que el ejemplo de `split` el valor no era apropiado para ningún caso de llamado.\n",
    "\n",
    "Es importante notar la sutil diferencia entre `ValueError` y `TypeError`. De cierta forma, todo `TypeError` es un `ValueError`, ya que detecta problemas del tipo de un argumento, que inherentemente habla del valor recibido, pero no todo `ValueError` es un `TypeError`. Prueba de esto último fueron los últimos dos ejemplos, donde los argumentos tenían un tipo apropiado para el contexto, pero no el valor.\n",
    "\n",
    "Pero si todo `TypeError` es un `ValueError`, ¿por qué definir y usar ambos? La respuesta radica a que es más conveniente siempre tener la mayor información posible sobre las excepciones que se lancen. `TypeError` es un caso específico de `ValueError`, por lo tanto, aporta más información sobre el problema que ocurrió en la ejecución del código y ayuda más a manejarlo después. `ValueError` es un error bastante genérico, y ocurre cada vez que se **espera** que el *input* recibido cumpla cierta propiedad y no lo hace."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
