Python & AI Tutorials Logo
Programación Python

24. Comprender errores y tracebacks

Los errores son una parte inevitable de la programación. Todo programador, desde principiante hasta experto, se los encuentra con regularidad. La diferencia entre pelearte con los errores y aprender de ellos está en entender lo que Python está intentando decirte cuando algo sale mal.

Cuando Python encuentra un problema en tu código, no se detiene en silencio: proporciona información detallada sobre qué salió mal, dónde ocurrió y, a menudo, pistas sobre por qué. Aprender a leer e interpretar estos mensajes de error es una de las habilidades más valiosas que puedes desarrollar como programador.

En este capítulo, exploraremos las dos categorías principales de errores que encontrarás: errores de sintaxis (problemas con la forma en que escribiste el código) y excepciones en tiempo de ejecución (problemas que ocurren mientras el código se está ejecutando). Aprenderemos a leer tracebacks —los informes detallados de errores de Python— y a entender cómo las excepciones cambian el flujo normal de tu programa. Lo más importante es que desarrollaremos una mentalidad de depuración que trate los errores no como fracasos, sino como información valiosa que te ayuda a escribir mejor código.

24.1) Errores de sintaxis vs excepciones en tiempo de ejecución

Python distingue entre dos tipos de problemas fundamentalmente distintos en tu código: errores de sintaxis y excepciones en tiempo de ejecución. Entender esta distinción te ayuda a diagnosticar problemas más rápido y a saber dónde buscar soluciones.

24.1.1) Qué son los errores de sintaxis

Un error de sintaxis ocurre cuando Python no puede entender tu código porque viola las reglas gramaticales del lenguaje. Igual que "The cat sat on the" es una frase incompleta en inglés, el código con errores de sintaxis es Python incompleto o mal formado que el intérprete no puede analizar.

Los errores de sintaxis se detectan antes de que tu programa se ejecute. Python lee primero todo tu script, comprobando que cumple las reglas del lenguaje. Si encuentra un error de sintaxis, se niega a ejecutar cualquier parte del código, ni siquiera las partes que están correctas.

Aquí tienes un ejemplo simple:

python
# ADVERTENCIA: Error de sintaxis - solo para demostración
# ERROR: Falta dos puntos después de la sentencia if
age = 25
if age >= 18
    print("You are an adult")

Cuando intentas ejecutar este código, Python informa inmediatamente:

  File "example.py", line 3
    if age >= 18
                ^
SyntaxError: expected ':'

Observa varias características clave de este mensaje de error:

  1. Archivo y número de línea: Python te dice exactamente dónde encontró el problema (line 3)
  2. Indicador visual: El acento circunflejo (^) señala dónde Python se confundió
  3. Tipo de error: SyntaxError identifica claramente esto como un problema de gramática
  4. Pista útil: expected ':' te dice qué falta

El código nunca se ejecuta porque Python ni siquiera puede comenzar a ejecutarlo: la sintaxis no es válida.

Veamos otro error de sintaxis común:

python
# ADVERTENCIA: Error de sintaxis - solo para demostración
# ERROR: Paréntesis que no coinciden
numbers = [1, 2, 3, 4, 5]
total = sum(numbers
print(f"Total: {total}")

Python informa:

  File "example.py", line 2
    total = sum(numbers
               ^
SyntaxError: '(' was never closed

Aquí, Python detectó que abrimos un paréntesis en la línea 2 pero nunca lo cerramos. El error se informa en la línea 2 (donde está el paréntesis sin cerrar), y el acento circunflejo señala dónde Python esperaba encontrar el paréntesis de cierre.

Características clave de los errores de sintaxis:

  • Se detectan antes de que se ejecute cualquier código
  • Evitan que se ejecute todo el programa
  • Normalmente indican erratas, puntuación faltante o sangría incorrecta
  • La ubicación del error puede estar ligeramente después del error real

24.1.2) Qué son las excepciones en tiempo de ejecución

Una excepción en tiempo de ejecución (o simplemente "excepción") ocurre cuando un código sintácticamente correcto encuentra un problema durante la ejecución. El código es Python gramaticalmente válido, pero algo sale mal cuando el programa realmente se ejecuta.

A diferencia de los errores de sintaxis, las excepciones ocurren mientras tu programa se está ejecutando. Python analizó tu código correctamente y comenzó a ejecutarlo, pero luego encontró una situación que no pudo manejar.

Aquí tienes un ejemplo simple:

python
# Este código tiene sintaxis válida pero generará una excepción
numbers = [10, 20, 30]
print(numbers[0])  # Output: 10
print(numbers[5])  # Esta línea generará un IndexError
print("This line never executes")

Output:

10
Traceback (most recent call last):
  File "example.py", line 3, in <module>
    print(numbers[5])
          ~~~~~~~^^^
IndexError: list index out of range

Observa lo que pasó:

  1. La primera sentencia print se ejecutó correctamente (vimos 10)
  2. La segunda print intentó acceder al índice 5, que no existe
  3. Python lanzó una excepción IndexError
  4. El programa se detuvo, y la tercera print nunca se ejecutó

El código era sintácticamente correcto: Python no tuvo problema en entender lo que queríamos hacer. El problema surgió durante la ejecución cuando intentamos acceder a un elemento de una lista que no existía.

Aquí tienes otro ejemplo que muestra un tipo distinto de excepción en tiempo de ejecución:

python
# Sintaxis válida, pero división por cero en tiempo de ejecución
def calculate_average(total, count):
    return total / count
 
# Estas funcionan bien
print(calculate_average(100, 4))  # Output: 25.0
print(calculate_average(75, 3))   # Output: 25.0
 
# Esta genera una excepción
print(calculate_average(50, 0))   # ZeroDivisionError

Output:

25.0
25.0
Traceback (most recent call last):
  File "example.py", line 8, in <module>
    print(calculate_average(50, 0))
          ^^^^^^^^^^^^^^^^^^^^^^^^
  File "example.py", line 2, in calculate_average
    return total / count
           ~~~~~~^~~~~~~
ZeroDivisionError: division by zero

La función funcionó perfectamente dos veces, pero en la tercera llamada pasamos 0 como el recuento, provocando una división por cero. Python no pudo detectar este problema hasta que el código realmente se ejecutó con esos valores específicos.

Características clave de las excepciones en tiempo de ejecución:

  • Ocurren durante la ejecución del programa
  • El código es sintácticamente válido
  • A menudo dependen de datos o condiciones específicas
  • El programa se ejecuta hasta el punto en el que ocurre la excepción
  • Entradas diferentes pueden causar excepciones diferentes (o ninguna)

24.1.3) Comparar errores de sintaxis y excepciones en tiempo de ejecución

Veamos ambos tipos de errores lado a lado para entender sus diferencias:

python
# Ejemplo 1: Error de sintaxis
# ERROR: Falta la comilla de cierre
print("Program started!")
message = "Hello, world
print(message)

Esto produce un error de sintaxis inmediatamente:

  File "example.py", line 4
    message = "Hello, world
              ^
SyntaxError: unterminated string literal (detected at line 4)

Importante: Observa que no ves "Program started!" en la salida. Python detectó el error de sintaxis antes de ejecutar cualquier código.

Ahora compáralo con una excepción en tiempo de ejecución:

python
# Ejemplo 2: Excepción en tiempo de ejecución
# Sintaxis válida, pero la variable no existe
print("Program started!")
message = "Hello, world"
print(mesage)  # Errata: 'mesage' en lugar de 'message'

Output:

Program started!
Traceback (most recent call last):
  File "example.py", line 5, in <module>
    print(mesage)
          ^^^^^^
NameError: name 'mesage' is not defined

Importante: Esta vez ves "Program started!" en la salida. Python ejecutó correctamente las dos primeras sentencias print y de asignación (líneas 3-4), pero se encontró con un problema en la línea 5 al intentar encontrar mesage.

La diferencia clave: En el primer ejemplo, Python ni siquiera intentó ejecutar el código: encontró el error de sintaxis durante el análisis. En el segundo ejemplo, Python comenzó a ejecutar el programa y ejecutó varias líneas antes de encontrarse con el error en tiempo de ejecución.

No

No

Python lee tu código

¿Sintaxis válida?

Error de sintaxis
El programa nunca se ejecuta

El programa empieza a ejecutarse

¿Problema durante
la ejecución?

El programa termina
correctamente

Excepción en tiempo de ejecución
El programa se detiene

24.2) Tipos comunes de excepciones integradas

Python tiene muchos tipos de excepciones integradas, cada una representa un tipo específico de problema. Aprender a reconocer estas excepciones comunes te ayuda a entender rápidamente qué salió mal y cómo arreglarlo. Cada tipo de excepción tiene un nombre descriptivo que da pistas sobre el problema.

24.2.1) NameError: usar nombres no definidos

Un NameError ocurre cuando intentas usar una variable, función u otro nombre que Python no reconoce. Esto normalmente significa que olvidaste definir algo, escribiste mal un nombre o intentas usar algo antes de que se cree.

python
# Ejemplo 1: Olvidaste definir una variable
print(greeting)  # NameError: name 'greeting' is not defined

Output:

Traceback (most recent call last):
  File "example.py", line 2, in <module>
    print(greeting)
          ^^^^^^^^
NameError: name 'greeting' is not defined

Python te está diciendo que no sabe qué es greeting. Primero tienes que crearlo:

python
# Versión correcta
greeting = "Hello, Python!"
print(greeting)  # Output: Hello, Python!

Aquí tienes un ejemplo más sutil con una errata:

python
# Ejemplo 2: Errata en el nombre de la variable
user_name = "Alice"
age = 30
 
print(f"{username} is {age} years old")  # NameError: name 'username' is not defined

Definimos user_name (con guion bajo) pero intentamos usar username (sin guion bajo). Python los ve como nombres completamente distintos.

24.2.2) TypeError: tipo incorrecto para una operación

Un TypeError ocurre cuando intentas realizar una operación sobre un valor del tipo incorrecto. Por ejemplo, no puedes sumar una cadena con un entero, o llamar a algo que no es una función.

python
# Ejemplo 1: Mezclar tipos incompatibles
age = 25
message = "You are " + age + " years old"  # TypeError

Output:

Traceback (most recent call last):
  File "example.py", line 2, in <module>
    message = "You are " + age + " years old"
              ~~~~~~~~~~~~^~~~~
TypeError: can only concatenate str (not "int") to str

Python te está diciendo que el operador + puede concatenar cadenas con cadenas, pero no cadenas con enteros. Tienes que convertir el entero a cadena:

python
# Versión correcta
age = 25
message = "You are " + str(age) + " years old"
print(message)  # Output: You are 25 years old

Los TypeError también ocurren cuando pasas un número incorrecto de argumentos a una función:

python
# Ejemplo 3: Número incorrecto de argumentos
def calculate_area(length, width):
    return length * width
 
area = calculate_area(5)  # TypeError: missing 1 required positional argument

Output:

Traceback (most recent call last):
  File "example.py", line 4, in <module>
    area = calculate_area(5)
TypeError: calculate_area() missing 1 required positional argument: 'width'

La función espera dos argumentos, pero solo proporcionamos uno.

24.2.3) ValueError: tipo correcto, valor incorrecto

Un ValueError ocurre cuando pasas un valor del tipo correcto, pero el valor en sí no es apropiado para la operación. El tipo es el correcto, pero el valor específico no tiene sentido en ese contexto.

python
# Ejemplo 1: Convertir una cadena inválida a entero
user_input = "twenty-five"
age = int(user_input)  # ValueError: invalid literal for int()

Output:

Traceback (most recent call last):
  File "example.py", line 2, in <module>
    age = int(user_input)
ValueError: invalid literal for int() with base 10: 'twenty-five'

La función int() espera una cadena, y le dimos una cadena, así que el tipo es correcto. Pero la cadena "twenty-five" no puede convertirse a un entero porque contiene letras. La cadena "25" funcionaría bien:

python
# Versión correcta
user_input = "25"
age = int(user_input)
print(age)  # Output: 25

Los ValueError también ocurren con métodos de listas:

python
# Ejemplo 3: Eliminar un elemento que no existe
fruits = ["apple", "banana", "orange"]
fruits.remove("grape")  # ValueError: 'grape' is not in list

Output:

Traceback (most recent call last):
  File "example.py", line 2, in <module>
    fruits.remove("grape")
    ~~~~~~~~~~~~~^^^^^^^^^
ValueError: list.remove(x): x not in list

El método remove() espera un valor que exista en la lista. Deberíamos comprobarlo primero:

python
# Versión correcta
fruits = ["apple", "banana", "orange"]
if "grape" in fruits:
    fruits.remove("grape")
else:
    print("Grape not found in list")  # Output: Grape not found in list

24.2.4) IndexError: índice inválido en una secuencia

Un IndexError ocurre cuando intentas acceder a una secuencia (lista, tupla, cadena) usando un índice que no existe. Recuerda que Python usa indexación basada en cero, y los índices válidos van de 0 a len(sequence) - 1.

python
# Ejemplo 1: Índice demasiado grande
colors = ["red", "green", "blue"]
print(colors[0])  # Output: red
print(colors[3])  # IndexError: list index out of range

Output:

red
Traceback (most recent call last):
  File "example.py", line 3, in <module>
    print(colors[3])
          ~~~~~~^^^
IndexError: list index out of range

La lista tiene tres elementos en los índices 0, 1 y 2. El índice 3 no existe. Este es un error muy común cuando olvidas que la indexación empieza en 0:

python
# Versión correcta
colors = ["red", "green", "blue"]
print(colors[2])  # Output: blue (the third element)

24.2.5) KeyError: falta una clave del diccionario

Un KeyError ocurre cuando intentas acceder a una clave de diccionario que no existe. A diferencia de las listas donde puedes comprobar la longitud, los diccionarios pueden tener cualquier clave, así que necesitas verificar que una clave existe antes de acceder a ella.

python
# Ejemplo 1: Acceder a una clave que no existe
student = {
    "name": "Alice",
    "age": 20,
    "major": "Computer Science"
}
 
print(student["name"])   # Output: Alice
print(student["grade"])  # KeyError: 'grade'

Output:

Alice
Traceback (most recent call last):
  File "example.py", line 7, in <module>
    print(student["grade"])
          ~~~~~~~^^^^^^^^^
KeyError: 'grade'

El diccionario no tiene una clave "grade". Puedes comprobar primero si una clave existe:

python
# Versión correcta usando 'in'
student = {
    "name": "Alice",
    "age": 20,
    "major": "Computer Science"
}
 
if "grade" in student:
    print(student["grade"])
else:
    print("Grade not available")  # Output: Grade not available

O usar el método get(), que devuelve None (o un valor predeterminado) en lugar de lanzar un error:

python
# Alternativa usando get()
grade = student.get("grade")
if grade is not None:
    print(f"Grade: {grade}")
else:
    print("Grade not available")  # Output: Grade not available

Los KeyError suelen ocurrir al procesar datos con estructura inconsistente:

python
# Ejemplo 2: Procesar múltiples registros
students = [
    {"name": "Alice", "age": 20, "grade": "A"},
    {"name": "Bob", "age": 21},  # Falta la clave 'grade'
    {"name": "Carol", "age": 19, "grade": "B"}
]
 
for student in students:
    print(f"{student['name']}: {student['grade']}")  # KeyError en Bob

Output:

Alice: A
Traceback (most recent call last):
  File "example.py", line 7, in <module>
    print(f"{student['name']}: {student['grade']}")
                                ~~~~~~~^^^^^^^^^
KeyError: 'grade'

Usa get() con un valor predeterminado para manejar claves ausentes con elegancia:

python
# Versión correcta
students = [
    {"name": "Alice", "age": 20, "grade": "A"},
    {"name": "Bob", "age": 21},
    {"name": "Carol", "age": 19, "grade": "B"}
]
 
for student in students:
    grade = student.get("grade", "Not assigned")
    print(f"{student['name']}: {grade}")

Output:

Alice: A
Bob: Not assigned
Carol: B

24.2.6) AttributeError: acceso inválido a un atributo

Un AttributeError ocurre cuando intentas acceder a un atributo o método que no existe en un objeto. Esto a menudo ocurre cuando confundes métodos entre distintos tipos o escribes mal nombres de atributos.

python
# Ejemplo 1: Método incorrecto para el tipo
numbers = [1, 2, 3, 4, 5]
numbers.append(6)  # Esto funciona: las listas tienen append()
print(numbers)     # Output: [1, 2, 3, 4, 5, 6]
 
text = "Hello"
text.append("!")   # AttributeError: 'str' object has no attribute 'append'

Output:

[1, 2, 3, 4, 5, 6]
Traceback (most recent call last):
  File "example.py", line 6, in <module>
    text.append("!")
    ^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'append'

Las cadenas no tienen un método append() porque son inmutables. Necesitas usar concatenación u otros métodos de cadenas:

python
# Versión correcta
text = "Hello"
text = text + "!"  # Concatenación
print(text)        # Output: Hello!

Los AttributeError también ocurren con erratas:

python
# Ejemplo 2: Nombre de método mal escrito
message = "Python Programming"
result = message.uppper()  # AttributeError: 'str' object has no attribute 'uppper'

Output:

Traceback (most recent call last):
  File "example.py", line 2, in <module>
    result = message.uppper()
             ^^^^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'uppper'. Did you mean: 'upper'?

Observa que Python 3.10+ a menudo sugiere la escritura correcta. El método correcto es upper():

python
# Versión correcta
message = "Python Programming"
result = message.upper()
print(result)  # Output: PYTHON PROGRAMMING

24.2.7) ZeroDivisionError: división por cero

Un ZeroDivisionError ocurre cuando intentas dividir un número entre cero, lo cual es matemáticamente indefinido. Esto a menudo sucede con entrada del usuario o valores calculados que no esperabas que fueran cero.

python
# Ejemplo 1: División directa entre cero
result = 10 / 0  # ZeroDivisionError: division by zero

Output:

Traceback (most recent call last):
  File "example.py", line 1, in <module>
    result = 10 / 0
             ~~~^~~
ZeroDivisionError: division by zero

Esto también aplica a la división entera y el módulo:

python
# Ejemplo 2: Otras operaciones de división
a = 10 // 0  # ZeroDivisionError
b = 10 % 0   # ZeroDivisionError

Un ejemplo más realista involucra cálculos:

python
# Ejemplo 3: Calcular el promedio
def calculate_average(numbers):
    total = sum(numbers)
    count = len(numbers)
    return total / count
 
scores = [85, 90, 78, 92]
print(calculate_average(scores))  # Output: 86.25
 
empty_scores = []
print(calculate_average(empty_scores))  # ZeroDivisionError

Output:

86.25
Traceback (most recent call last):
  File "example.py", line 9, in <module>
    print(calculate_average(empty_scores))
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "example.py", line 4, in calculate_average
    return total / count
           ~~~~~~^~~~~~~
ZeroDivisionError: division by zero

Una lista vacía tiene longitud 0, causando división por cero. Comprueba siempre esta condición:

python
# Versión correcta
def calculate_average(numbers):
    if len(numbers) == 0:
        return 0  # O return None, o lanzar un error más descriptivo
    
    total = sum(numbers)
    count = len(numbers)
    return total / count
 
scores = [85, 90, 78, 92]
print(calculate_average(scores))  # Output: 86.25
 
empty_scores = []
print(calculate_average(empty_scores))  # Output: 0

Tipos comunes de excepciones

NameError
Variable/función no definida

TypeError
Tipo incorrecto para la operación

ValueError
Tipo correcto, valor incorrecto

IndexError
Índice de secuencia inválido

KeyError
Falta una clave de diccionario

AttributeError
Atributo/método inválido

ZeroDivisionError
División por cero

Comprender estos tipos comunes de excepciones te ayuda a diagnosticar rápidamente problemas. Cuando ves una excepción, el nombre del tipo te dice inmediatamente qué categoría de problema ocurrió, y el mensaje de error proporciona detalles específicos sobre qué salió mal.

24.3) Leer e interpretar tracebacks en detalle

Cuando ocurre una excepción en tiempo de ejecución, Python no solo te dice qué salió mal: proporciona un traceback detallado que muestra exactamente cómo llegó tu programa a ese punto. Aprender a leer tracebacks es esencial para una depuración eficaz. Un traceback es como un rastro de migas de pan que muestra el camino que tomó tu programa antes de encontrarse con el error.

24.3.1) Anatomía de un traceback

Empecemos con un ejemplo simple y examinemos cada parte del traceback:

python
# Programa simple con un error
def calculate_discount(price, discount_percent):
    discount_amount = price * (discount_percent / 100)
    final_price = price - discount_amount
    return final_price
 
def process_order(item_price, discount):
    discounted_price = calculate_discount(item_price, discount)
    tax = discounted_price * 0.08
    total = discounted_price + tax
    return total
 
# Programa principal
original_price = "50"  # ¡Ups! Esto debería ser un número
discount_rate = 10
final_cost = process_order(original_price, discount_rate)
print(f"Final cost: ${final_cost:.2f}")

Output:

Traceback (most recent call last):
  File "example.py", line 16, in <module>
    final_cost = process_order(original_price, discount_rate)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "example.py", line 8, in process_order
    discounted_price = calculate_discount(item_price, discount)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "example.py", line 2, in calculate_discount
    discount_amount = price * (discount_percent / 100)
                      ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
TypeError: can't multiply sequence by non-int of type 'float'

Desglosemos cada componente de este traceback:

1. El encabezado: "Traceback (most recent call last):"

Esta línea te dice que lo que sigue es un traceback: un registro de llamadas a funciones. La frase "most recent call last" significa que el traceback se muestra en orden cronológico: la primera función llamada aparece primero, y el lugar donde el error realmente ocurrió aparece al final.

2. La pila de llamadas (leyendo de arriba hacia abajo):

  File "example.py", line 16, in <module>
    final_cost = process_order(original_price, discount_rate)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Esta es la primera llamada a función en la cadena. Muestra:

  • Nombre del archivo: "example.py" - donde está el código
  • Número de línea: line 16 - la línea exacta que hizo esta llamada
  • Contexto: in <module> - este código está en el nivel superior (no dentro de una función)
  • Código: La línea real que se ejecutó
  • Resaltado: Los caracteres ^ apuntan a la parte específica de la línea involucrada

El contexto <module> significa que este código se está ejecutando a nivel de módulo (la parte principal de tu script), no dentro de ninguna función.

  File "example.py", line 8, in process_order
    discounted_price = calculate_discount(item_price, discount)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Esta es la segunda llamada a función. La función process_order fue llamada desde la línea 16, y ahora estamos dentro de esa función en la línea 8, donde llama a calculate_discount.

  File "example.py", line 2, in calculate_discount
    discount_amount = price * (discount_percent / 100)
                      ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~

Aquí es donde el error realmente ocurrió. Ahora estamos dentro de la función calculate_discount en la línea 2, y esta es la línea que causó el problema.

3. El mensaje de error:

TypeError: can't multiply sequence by non-int of type 'float'

Este es el error real que ocurrió:

  • Tipo de excepción: TypeError - te dice la categoría del error
  • Descripción: El resto explica qué salió mal en concreto

En este caso, Python nos está diciendo que intentamos multiplicar una secuencia (una cadena, en este caso) por un float, lo cual no está permitido.

24.3.2) Leer el traceback de abajo hacia arriba

Aunque el traceback se imprime en orden cronológico (de arriba hacia abajo), los programadores con experiencia a menudo lo leen de abajo hacia arriba porque el error real está abajo, y las líneas de arriba muestran cómo llegamos ahí.

Leamos nuestro traceback anterior de abajo hacia arriba:

Paso 1: Empieza con el mensaje de error

TypeError: can't multiply sequence by non-int of type 'float'

"Vale, intentamos multiplicar una secuencia por un float. Eso no está permitido."

Paso 2: Mira dónde ocurrió el error

  File "example.py", line 2, in calculate_discount
    discount_amount = price * (discount_percent / 100)
                      ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~

"El error ocurrió en la función calculate_discount en la línea 2. Estamos intentando multiplicar price por algo."

Paso 3: Retrocede para ver cómo llegamos ahí

  File "example.py", line 8, in process_order
    discounted_price = calculate_discount(item_price, discount)

"La función calculate_discount se llamó desde process_order en la línea 8, pasando item_price como el parámetro price."

Paso 4: Sigue retrocediendo

  File "example.py", line 16, in <module>
    final_cost = process_order(original_price, discount_rate)

"Y process_order se llamó desde el programa principal en la línea 16, pasando original_price como item_price."

Paso 5: Encuentra la causa raíz

Ahora podemos rastrear el problema: original_price es "50" (una cadena), que se pasa como item_price a process_order, que lo pasa como price a calculate_discount, donde intentamos multiplicarlo por un float. La solución es hacer que original_price sea un número:

python
# Versión corregida
def calculate_discount(price, discount_percent):
    discount_amount = price * (discount_percent / 100)
    final_price = price - discount_amount
    return final_price
 
def process_order(item_price, discount):
    discounted_price = calculate_discount(item_price, discount)
    tax = discounted_price * 0.08
    total = discounted_price + tax
    return total
 
# Programa principal: se corrigió el tipo
original_price = 50  # Ahora es un número, no una cadena
discount_rate = 10
final_cost = process_order(original_price, discount_rate)
print(f"Final cost: ${final_cost:.2f}")  # Output: Final cost: $48.60

Leer traceback

1. Leer el mensaje de error
Línea inferior
2. Encontrar la ubicación del error
Último bloque de código
3. Seguir la pila de llamadas
Subir hacia arriba
4. Formular hipótesis
¿Qué salió mal?
5. Verificar y corregir
Probar tu solución

Entender cómo leer tracebacks los transforma de muros de texto intimidantes en herramientas útiles de depuración. Cada línea proporciona información valiosa sobre el camino de ejecución de tu programa y, con práctica, podrás identificar y corregir problemas rápidamente siguiendo la guía del traceback.

24.4) Cómo las excepciones cambian el flujo normal de un programa

Cuando ocurre una excepción, no solo detiene tu programa: cambia fundamentalmente cómo se ejecuta. Entender este comportamiento es crucial para escribir código robusto y para comprender qué sucede cuando ocurren errores.

24.4.1) Flujo normal del programa vs flujo con excepciones

En una ejecución normal, Python ejecuta tu código línea por línea, de arriba hacia abajo:

python
# Flujo normal del programa
print("Step 1: Starting calculation")
result = 10 + 5
print(f"Step 2: Result is {result}")
final = result * 2
print(f"Step 3: Final value is {final}")
print("Step 4: Program complete")

Output:

Step 1: Starting calculation
Step 2: Result is 15
Step 3: Final value is 30
Step 4: Program complete

Cada línea se ejecuta en orden. Ahora veamos qué pasa cuando ocurre una excepción:

python
# Flujo del programa con una excepción
print("Step 1: Starting calculation")
result = 10 / 0  # Esto genera ZeroDivisionError
print(f"Step 2: Result is {result}")  # Esto nunca se ejecuta
final = result * 2  # Esto nunca se ejecuta
print(f"Step 3: Final value is {final}")  # Esto nunca se ejecuta
print("Step 4: Program complete")  # Esto nunca se ejecuta

Output:

Step 1: Starting calculation
Traceback (most recent call last):
  File "example.py", line 2, in <module>
    result = 10 / 0
             ~~~^~~
ZeroDivisionError: division by zero

Observa que solo se ejecutó la primera sentencia print. En cuanto ocurrió la excepción en la línea 2, Python dejó de ejecutar el resto del código. La excepción interrumpió el flujo normal.

24.4.2) Las excepciones se propagan hacia arriba en la pila de llamadas

Cuando ocurre una excepción dentro de una función, Python no se detiene solo en esa función: la propaga (viaja hacia arriba) por la pila de llamadas hasta que algo la maneje o el programa termine.

python
# Ejemplo 1: Excepción que se propaga a través de funciones
def calculate_average(numbers):
    total = sum(numbers)
    count = len(numbers)
    return total / count  # Podría generar ZeroDivisionError
 
def process_scores(score_list):
    print("Processing scores...")
    avg = calculate_average(score_list)
    print(f"Average calculated: {avg}")
    return avg
 
def main():
    print("Program starting")
    scores = []  # Lista vacía
    result = process_scores(scores)
    print(f"Final result: {result}")
    print("Program ending")
 
main()

Output:

Program starting
Processing scores...
Traceback (most recent call last):
  File "example.py", line 18, in <module>
    main()
  File "example.py", line 14, in main
    result = process_scores(scores)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "example.py", line 9, in process_scores
    avg = calculate_average(score_list)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "example.py", line 4, in calculate_average
    return total / count
           ~~~~~~^~~~~~~
ZeroDivisionError: division by zero

Rastreemos lo que pasó:

  1. main() empezó a ejecutarse e imprimió "Program starting"
  2. main() llamó a process_scores()
  3. process_scores() imprimió "Processing scores..."
  4. process_scores() llamó a calculate_average()
  5. calculate_average() intentó dividir por cero
  6. Ocurrió la excepción y se propagó hacia arriba:
    • calculate_average() se detuvo inmediatamente (no devolvió un valor)
    • El control volvió a process_scores(), pero no de forma normal: la excepción siguió propagándose
    • process_scores() se detuvo inmediatamente (la impresión después de calculate_average() nunca se ejecutó)
    • El control volvió a main(), pero de nuevo, la excepción siguió propagándose
    • main() se detuvo inmediatamente (las impresiones después de process_scores() nunca se ejecutaron)
  7. El programa terminó con el traceback

Ninguno del código después de la excepción se ejecutó en ninguna de las funciones. La excepción fue "subiendo" por todas las llamadas a funciones hasta llegar al nivel superior y terminar el programa.

24.5) Mentalidad de depuración: tratar los errores como información, no como fracasos

Una de las habilidades más importantes en programación no es escribir código perfecto: es aprender a trabajar de forma efectiva con código imperfecto. Todo programador, independientemente de su nivel de experiencia, escribe código que produce errores. La diferencia entre los programadores que se atascan y los que son eficaces no está en evitar errores, sino en cómo responden a ellos.

24.5.1) Los errores no son fracasos

Cuando estás aprendiendo a programar, es natural sentir frustración cuando te encuentras con errores. Puede que sientas que has hecho algo mal o que no lo estás "pillando". Esta mentalidad es contraproducente y, más importante aún, inexacta.

Los errores no son fracasos: son retroalimentación.

Piensa en los errores como un GPS recalculando tu ruta. Cuando te saltas un giro, el GPS no dice "¡Has fracasado!". Dice "Recalculating route" y te da nuevas indicaciones. Los mensajes de error de Python funcionan igual: te están diciendo que el camino que tomaste no funcionó y te están proporcionando información para ayudarte a encontrar un camino que sí funcione.

Considera este ejemplo simple:

python
# Primer intento de calcular el promedio
def calculate_average(numbers):
    total = sum(numbers)
    average = total / len(numbers)
    return average
 
scores = []
result = calculate_average(scores)
print(f"Average: {result}")

Output:

Traceback (most recent call last):
  File "example.py", line 8, in <module>
    result = calculate_average(scores)
             ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "example.py", line 4, in calculate_average
    average = total / len(numbers)
              ~~~~~~^~~~~~~~~~~~~~
ZeroDivisionError: division by zero

Este error no te está diciendo que seas un mal programador. Es decirte algo específico y útil: "Intentaste dividir por cero, lo cual ocurre cuando la lista está vacía. Necesitas manejar ese caso."

Con esta información, puedes mejorar tu código:

python
# Versión mejorada basada en la retroalimentación del error
def calculate_average(numbers):
    if len(numbers) == 0:
        return 0  # O return None, o lanzar un error más descriptivo
    
    total = sum(numbers)
    average = total / len(numbers)
    return average
 
scores = []
result = calculate_average(scores)
print(f"Average: {result}")  # Output: Average: 0

El error te ayudó a escribir mejor código. Sin ese error, quizá no te habrías dado cuenta de que tu función no podía manejar listas vacías.

24.5.2) Cada error te enseña algo

Cada error que encuentras te enseña algo sobre Python, sobre tu código o sobre la programación en general. Veamos varios ejemplos de lo que distintos errores nos enseñan:

Ejemplo 1: Aprender sobre tipos

python
# Intentar sumar tipos incompatibles
age = 25
message = "You are " + age + " years old"

Output:

TypeError: can only concatenate str (not "int") to str

Qué te enseña esto: Python tiene reglas estrictas de tipos. No puedes mezclar cadenas y números en una concatenación. Este error te enseña sobre compatibilidad de tipos y te introduce al concepto de conversión de tipos.

Ejemplo 2: Aprender sobre estructuras de datos

python
# Intentar acceder a un diccionario como si fuera una lista
student = {"name": "Alice", "age": 20}
first_value = student[0]

Output:

KeyError: 0

Qué te enseña esto: Los diccionarios usan claves, no índices numéricos. Este error te enseña la diferencia entre diccionarios y listas, y cómo acceder correctamente a valores de un diccionario.

Ejemplo 3: Aprender sobre alcance

python
# Intentar usar una variable antes de definirla
def greet():
    print(f"Hello, {name}!")
 
greet()
name = "Alice"

Output:

NameError: name 'name' is not defined

Qué te enseña esto: Las variables deben definirse antes de usarse, y el orden de ejecución importa. Este error te enseña sobre alcance de variables y la importancia de la inicialización.

Cada uno de estos errores proporciona información específica y accionable que te ayuda a entender Python mejor. En lugar de verlos como obstáculos, míralos como oportunidades de aprendizaje.

24.5.3) Adoptar la mentalidad de depuración

Los programadores profesionales pasan una parte importante de su tiempo depurando. No es una señal de debilidad: es una parte esencial del trabajo. Los mejores programadores no son los que nunca cometen errores; son los que:

  1. Esperan errores: saben que los errores van a ocurrir y no se sorprenden ni se desaniman
  2. Leen los errores con cuidado: extraen la máxima información de los mensajes de error
  3. Depuran de forma sistemática: siguen un proceso lógico en lugar de hacer cambios al azar
  4. Aprenden de los errores: usan cada error como una oportunidad para entender Python mejor
  5. Se mantienen curiosos: preguntan "¿Por qué pasó eso?" en lugar de solo "¿Cómo lo arreglo?"

No

No

Encontrar un error

Leer el mensaje de error
con cuidado

Entender qué
salió mal

Formular una hipótesis
sobre la causa

Probar la hipótesis
con salida de depuración

¿Hipótesis
correcta?

Implementar la corrección

Probar la solución

¿Funciona
correctamente?

Aprender de la
experiencia

Avanzar
con confianza

Recuerda: cada error es una oportunidad para aprender algo nuevo sobre Python, sobre programación o sobre resolución de problemas. Acepta los errores como retroalimentación valiosa, abórdalos de forma sistemática y celebra tus éxitos depurando. Esta mentalidad te servirá durante todo tu camino en programación.


Comprender los errores y tracebacks es fundamental para convertirte en un programador de Python eficaz. En este capítulo, hemos aprendido a distinguir entre errores de sintaxis (problemas con la estructura del código) y excepciones en tiempo de ejecución (problemas durante la ejecución), reconocer tipos comunes de excepciones y lo que indican, leer e interpretar tracebacks detallados para encontrar la causa raíz de los problemas, entender cómo las excepciones cambian el flujo del programa al propagarse hacia arriba en la pila de llamadas, y desarrollar una mentalidad de depuración que trate los errores como información valiosa en lugar de fracasos.

Estas habilidades forman la base para el siguiente capítulo, donde aprenderemos a manejar excepciones con elegancia usando bloques try y except, permitiendo que nuestros programas se recuperen de errores y sigan ejecutándose. Pero antes de poder manejar excepciones de forma efectiva, necesitamos entenderlas a fondo, y eso es exactamente lo que hemos logrado aquí.

© 2025. Primesoft Co., Ltd.
support@primesoft.ai