La indentación como sintaxis significativa en Python

Cuando ves código Python por primera vez y no hay llaves {} ni palabras clave como end o begin, la pregunta natural es: ¿cómo sabe Python dónde termina un bloque? La respuesta es que la indentación es sintaxis. No es estilo opcional ni convención de equipo — el intérprete la lee y la analiza igual que lee if o for.

Un bloque de código es un conjunto de instrucciones que pertenecen a la misma estructura de control: el cuerpo de un if, el cuerpo de una función, el cuerpo de un bucle. En C o Java eso se marca con { y }. En Python se marca aumentando el nivel de sangría después de los dos puntos :, y ese nivel debe mantenerse constante hasta que el bloque termine.

El diseño no es arbitrario. Guido van Rossum tomó una decisión deliberada: si el compilador va a leer la indentación de todas formas para que el código sea legible, ¿para qué duplicar esa información con llaves? Al eliminar las llaves, Python hace imposible escribir código visualmente engañoso como el famoso bug de goto fail de Apple, donde una llave mal colocada ocultaba código muerto en una función de seguridad crítica.

PEP 8, la guía de estilo oficial, establece 4 espacios por nivel de indentación. No tabulaciones, no 2 espacios, no 8: cuatro espacios. El intérprete acepta cualquier cantidad consistente, pero toda la comunidad y todas las herramientas esperan 4 espacios, así que apartarte de eso tiene un coste social y de tooling real.

Lo que sí rompe el intérprete sin piedad es mezclar tabulaciones y espacios dentro del mismo archivo. Python 3 lo convirtió en un error explícito: si un bloque usa una tabulación y otro usa espacios, obtienes TabError en el momento de importar o ejecutar el módulo. La razón es que un tab puede visualizarse como 4 u 8 columnas dependiendo del editor, pero el intérprete lo cuenta como un carácter distinto al espacio — y esa ambigüedad produce comportamiento impredecible, así que Python 3 optó por prohibirlo en lugar de adivinar.

# indentation_demo.py

def classify_temperature(temp: float) -> str:
    if temp < 0:
        category = "freezing"
        warning = "pipes may burst"      # mismo bloque: mismo nivel
    elif temp < 15:
        category = "cold"
        warning = "dress in layers"
    elif temp < 25:
        category = "comfortable"
        warning = ""
    else:
        category = "hot"
        warning = "stay hydrated"

    # este return está al nivel de la función, no del if/elif/else
    # Python lo sabe porque la indentación volvió a 4 espacios
    if warning:
        return f"{category} ({warning})"
    return category


def check_forecast(temperatures: list[float]) -> None:
    for temp in temperatures:
        result = classify_temperature(temp)
        print(f"{temp:5.1f}°C → {result}")

    # este print está fuera del for: solo se ejecuta una vez
    print("---forecast complete---")


if __name__ == "__main__":
    forecast = [-3.0, 8.5, 22.0, 37.2]
    check_forecast(forecast)

Desglose del código

Fíjate en classify_temperature: después de if temp < 0: hay dos líneas indentadas con 8 espacios (4 del cuerpo de la función + 4 del cuerpo del if). Ambas tienen el mismo nivel, así que ambas pertenecen al mismo bloque. Cuando aparece elif, la indentación vuelve a 4 — Python interpreta ese descenso como el cierre del bloque anterior.

El return final de la función vive a 4 espacios. Python no necesita una llave de cierre para saber que el if/elif/else terminó: el nivel de indentación lo declara sin ambigüedad.

En check_forecast, el print("---forecast complete---") está a 4 espacios, mismo nivel que el for. Eso lo coloca fuera del bucle. Si lo indentas un nivel más (8 espacios), pasa a ejecutarse en cada iteración. Ese cambio de significado con solo cuatro espacios es exactamente el poder y la responsabilidad que da el modelo de Python.

El bloque if __name__ == "__main__": al final es el punto de entrada estándar para scripts. Todo lo que está indentado bajo él solo corre cuando ejecutas el archivo directamente, no cuando lo importas como módulo.

Errores que debes conocer

Error: mezclar una tabulación con espacios en el mismo bloque hace que Python 3 lance TabError porque son caracteres distintos para el parser.

# ❌ Wrong  (el segundo print usa un tab, el primero usa espacios)
def greet(name):
    print("Hello")
	print(name)      # ← tab aquí, espacios arriba

# ✅ Right
def greet(name):
    print("Hello")
    print(name)      # ambas líneas: 4 espacios exactos

Configura tu editor para insertar espacios al presionar Tab — en VS Code es "editor.insertSpaces": true y "editor.tabSize": 4.


Error: indentar el código de retorno un nivel de más hace que quede dentro del bucle y se ejecute en cada iteración en lugar de al final.

# ❌ Wrong: el return está dentro del for, sale en la primera iteración
def first_positive(numbers):
    for n in numbers:
        if n > 0:
            return n
        return None    # ← indentado al nivel del for, no de la función

# ✅ Right: return None solo se alcanza si el bucle termina sin éxito
def first_positive(numbers):
    for n in numbers:
        if n > 0:
            return n
    return None        # ← 4 espacios: nivel de la función

Cuatro espacios de diferencia cambian completamente la lógica; Python te obliga a ser explícito con esa decisión.

14

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio