La lección anterior terminaba con una promesa —ese if que usamos «de puntillas» se explica a fondo aquí— y con un diagnóstico: TareaFácil detecta los datos incorrectos, pero solo sabe avisar de ellos y poner un valor por defecto. Avisar no es decidir.

Un programa que ejecuta sus instrucciones en orden, de la primera a la última, siempre hace lo mismo. Los programas útiles eligen: si el dato es válido lo usan, si no lo corrigen; si la tarea es de prioridad alta la marcan como urgente, si no la dejan en la cola normal. Esa capacidad de elegir es la primera de las dos estructuras de control de este módulo; la otra, repetir, llega en Bucles. Aquí aprenderás la sintaxis completa de if, elif y else, por qué la indentación en Python es gramática y no estética, cómo combinar condiciones y cuándo anidar decisiones o aplanarlas.

Contenido

  1. Qué es una decisión en un programa
  2. El if: condición, dos puntos e indentación
  3. if / else: el camino alternativo
  4. if / elif / else: alternativas excluyentes
  5. Por qué elif no es lo mismo que varios if
  6. Condiciones compuestas con and, or y not
  7. Condicionales anidados y cómo aplanarlos
  8. El operador ternario y la comparación encadenada
  9. TareaFácil v0.5.1: decidir en vez de avisar
  10. Errores comunes y consejos
  11. Ejercicios
  12. Conclusión

  1. Qué es una decisión en un programa

Una decisión es un punto donde el flujo se bifurca: se evalúa una condición y, según sea verdadera o falsa, se ejecuta un bloque de instrucciones u otro. La condición es siempre una expresión booleana de las que construiste en Operadores y expresiones: dias > 5, prioridad == "alta", responsable in EQUIPO.

flowchart TD
    A["Instruccion anterior"] --> B{"prioridad == alta ?"}
    B -->|True| C["Marcar como urgente"]
    B -->|False| D["Dejar en cola normal"]
    C --> E["Instruccion siguiente"]
    D --> E

Tres detalles del diagrama describen lo que hace el código: el rombo tiene solo dos salidas, cada rama hace algo distinto, y las dos ramas vuelven a juntarse, porque después de la decisión el programa continúa por un único camino. En Del problema al algoritmo dibujaste diagramas así en pseudocódigo; ahora los escribiremos en Python.

  1. El if: condición, dos puntos e indentación

La forma más simple ejecuta un bloque solo si se cumple una condición, y no hace nada en caso contrario.

dias_estimados = 12

if dias_estimados > 10:
    print("Tarea larga: conviene partirla en subtareas.")
print("Fin del analisis.")

Con dias_estimados = 3 la primera línea no se imprime y solo veríamos Fin del analisis.. La cabecera tiene tres piezas obligatorias —la palabra if, la condición y los dos puntos finales, y olvidar cualquiera de ellas da SyntaxError— más una cuarta que no está en la línea: el bloque indentado que va debajo.

La indentación es sintaxis

En la mayoría de lenguajes el bloque se delimita con llaves { } y la sangría es estética. En Python la sangría delimita el bloque. No hay llaves.

prioridad = "baja"

if prioridad == "alta":
    print("Linea A: dentro del if")
    print("Linea B: dentro del if")
print("Linea C: fuera del if")

Aquí solo se imprime la línea C: A y B están dentro del bloque porque están sangradas; C vuelve al margen izquierdo y se ejecuta siempre, valga lo que valga prioridad. La convención oficial (la guía PEP 8, que verás en Estilo y refactorización) es 4 espacios por nivel. No 2, no 8, no tabuladores; VS Code convierte el tabulador en 4 espacios automáticamente. Si te equivocas, Python no adivina: dejar vacío el bloque de un if aborta con IndentationError: expected an indented block after 'if' statement on line 1. Sus variantes son unexpected indent (una línea más sangrada de lo que toca), unindent does not match any outer indentation level (has cerrado el bloque a una sangría que no existe) y TabError (has mezclado tabuladores y espacios). Los cuatro se arreglan mirando la sangría, nunca la lógica.

La rigidez tiene recompensa: en Python, código bien indentado y código correcto son lo mismo. No puede ocurrir que la sangría diga una cosa y las llaves otra, que es un clásico en otros lenguajes.

  1. if / else: el camino alternativo

else define lo que ocurre cuando la condición es falsa. No lleva condición propia —significa «en cualquier otro caso»— y su cabecera es simplemente else:.

if horas_dedicadas <= horas_estimadas:
    print("Dentro del presupuesto de horas.")
else:
    print("Presupuesto de horas superado.")

Las dos ramas son excluyentes y exhaustivas: siempre se ejecuta una, nunca las dos, y no queda ningún caso sin cubrir. Con un if arreglamos ya un defecto de la versión 0.5, donde los avisos vacíos imprimían líneas en blanco: basta con envolver el print en un if aviso_prioridad != "":. Y como la cadena vacía es falsa (reglas de veracidad de Conversión de tipos y validación), lo idiomático es dejar que el propio valor haga de condición, if aviso_prioridad:. Ambas formas valen; la primera es más explícita para quien empieza.

  1. if / elif / else: alternativas excluyentes

Cuando hay más de dos caminos, elif (contracción de else if) encadena condiciones. Se evalúan de arriba abajo y se ejecuta solo la primera verdadera; el resto ni se comprueba.

prioridad = "media"

if prioridad == "alta":
    plazo = 1
elif prioridad == "media":
    plazo = 3
elif prioridad == "baja":
    plazo = 10
else:
    plazo = 3
    print("Prioridad desconocida. Se aplica el plazo estandar.")

print(f"Dias de plazo: {plazo}")     # Dias de plazo: 3

En el diagrama de flujo, cada rombo cuelga de la salida False del anterior: una condición solo se evalúa si todas las previas fallaron, y todas las ramas desembocan en el mismo punto. Puede haber cualquier número de elif, incluido ninguno; el else final es opcional, pero conviene ponerlo casi siempre como red de seguridad para el caso imprevisto. Y el orden importa. Si escribieras if dias > 5: seguido de elif dias > 15:, la segunda rama sería código muerto: ningún valor la alcanzaría nunca, porque todo lo mayor que 15 también es mayor que 5 y la primera rama se lo lleva. La regla es colocar primero las condiciones más restrictivas.

  1. Por qué elif no es lo mismo que varios if

Este es el punto que más confusión genera. Mismo dato, dos versiones que solo se diferencian en tres letras:

dias = 20
# --- Version con elif ---            # --- Version con if sueltos ---
if dias > 15:                         # if dias > 15:
    categoria = "muy larga"           #     categoria = "muy larga"
elif dias > 5:                        # if dias > 5:
    categoria = "larga"               #     categoria = "larga"
else:                                 # else:
    categoria = "corta"               #     categoria = "corta"
print(categoria)   # muy larga        # print(categoria)   # larga (!)

Con elif, la primera condición verdadera gana y las demás se saltan. Con if independientes cada uno se evalúa por separado: el primero asigna "muy larga" y el segundo, también cierto, lo pisa con "larga". Además, ese else final ya no pertenece al primer if, sino al segundo.

Aspecto if / elif / else Varios if seguidos
Ramas que se ejecutan Como mucho una Todas las que sean ciertas
Condiciones posteriores No se evalúan si una acertó Se evalúan siempre
Uso típico Clasificar un valor en categorías (excluyentes) Comprobar datos distintos (independientes)

Pregúntate siempre: ¿son alternativas del mismo dato, o comprobaciones independientes? Clasificar una prioridad en alta/media/baja son alternativas: elif. Validar el título, el responsable y los días son comprobaciones independientes: if separados, como en TareaFácil.

  1. Condiciones compuestas con and, or y not

Una condición puede combinar varias comprobaciones con los operadores lógicos de 02-02. and exige que todas las partes sean verdaderas; or se conforma con una; not invierte. Así, if prioridad == "alta" and not completada: selecciona las urgencias pendientes, y if prioridad == "baja" or dias > 15: las candidatas a aplazarse.

Se evalúan con cortocircuito: en A and B, si A es falso, B ni se mira. Eso no es un detalle académico, es una herramienta de protección:

if dias_texto.strip().isdigit() and int(dias_texto) > 0:
    print("Dias validos")

Si el texto no contiene dígitos, la primera parte es False y Python no ejecuta int(dias_texto), que habría lanzado un ValueError. Invierte el orden de las dos partes y el programa se rompe. La regla: comprueba primero lo que hace segura la comprobación siguiente. Y recuerda el error clásico de 02-02, que dentro de un if se paga caro: if prioridad == "alta" or "media": es siempre verdadero, porque "media" es un texto no vacío y por tanto verdadero por sí mismo. La forma correcta es if prioridad in ("alta", "media"):.

  1. Condicionales anidados y cómo aplanarlos

Un if puede contener otro dentro de su bloque; cada nivel añade 4 espacios de sangría.

if not completada:
    if prioridad == "alta":
        print(f"Urgencia pendiente de {responsable}.")
    else:
        print("Tarea pendiente, sin urgencia.")
else:
    print("Tarea completada.")

Funciona, pero para saber en qué caso estás hay que llevar varias condiciones en la cabeza a la vez, y la regla del oficio es que más de dos o tres niveles de anidamiento son una señal de alarma. Muchos se aplanan invirtiendo las preguntas y encadenando elif:

if completada:
    print("Tarea completada.")
elif prioridad != "alta":
    print("Tarea pendiente, sin urgencia.")
else:
    print(f"Urgencia pendiente de {responsable}.")

La versión anidada se lee como un árbol y es preferible cuando la pregunta interior solo tiene sentido si la exterior es cierta; la plana se lee como una lista de casos y es preferible cuando las condiciones describen alternativas. Elige la que un compañero entendería más rápido.

  1. El operador ternario y la comparación encadenada

Cuando la decisión consiste solo en elegir un valor entre dos, existe una forma abreviada de una línea, la expresión condicional o ternario, con la forma valor_si if condicion else valor_no:

completada = False
estado = "Completada" if completada else "Pendiente"    # Pendiente

Equivale a un if / else de cuatro líneas y se lee de forma casi natural: «estado es "Completada" si completada, si no "Pendiente"». Su ventaja es que, al ser una expresión y no una sentencia, cabe dentro de una f-string: print(f"Tarea {'larga' if dias > 10 else 'corta'}") imprime Tarea larga cuando dias vale 12. Úsalo para elegir entre dos valores simples; si cada rama debe ejecutar varias instrucciones, o hay más de dos alternativas, vuelve al if / elif / else. Y nunca anides un ternario dentro de otro: se vuelve ilegible.

Comparación encadenada. Para comprobar un rango, Python permite encadenar comparaciones como en matemáticas: if 1 <= dias <= 365:. Equivale a dias >= 1 and dias <= 365, pero se lee mejor y evalúa dias una sola vez. El if dias < 1 or dias > 365 de la versión 0.5 se escribe ahora if not (1 <= dias <= 365). Solo es legible con comparaciones del mismo sentido sobre el mismo dato, y no lo confundas con in: el encadenamiento comprueba un rango continuo, in comprueba pertenencia a un catálogo.

  1. TareaFácil v0.5.1: decidir en vez de avisar

La versión 0.5 detectaba los problemas y respondía siempre igual: valor por defecto. Ahora decidimos de verdad, con tres mejoras. Ante una prioridad inválida, si es un sinónimo reconocible (urgente, critica) se interpreta como alta, y solo si es irreconocible se aplica el valor por defecto. Ante unos días fuera de rango, se distingue demasiado pequeño (se sube al mínimo) de demasiado grande (se baja al máximo), en vez de sustituirlos por un número sin relación con lo introducido. Y ante un responsable ajeno al equipo, la tarea pasa a Marta, la coordinadora, y se marca para reasignar.

Además clasificamos la tarea en urgente, normal o aplazable, y los avisos vacíos dejan de imprimir líneas en blanco.

# tareafacil.py - Estudio Alba
# Version 0.5.1: el programa decide, no solo avisa

ANCHO = 46
HORAS_POR_JORNADA = 8
PRIORIDADES = ("alta", "media", "baja")
SINONIMOS_ALTA = ("urgente", "urgentisima", "critica", "critico")
EQUIPO = ("Marta", "Luis", "Nuria")
COORDINADORA = "Marta"
DIAS_MIN = 1
DIAS_MAX = 365
DIAS_POR_DEFECTO = 5

# --- 1. Titulo: presencia ---
titulo = input("Titulo de la tarea          : ").strip()
titulo = titulo if titulo != "" else "(sin titulo)"

# --- 2. Responsable: dominio, con reasignacion razonada ---
responsable = input("Responsable (Marta/Luis/Nuria): ").strip().capitalize()
aviso_responsable = ""
requiere_reasignacion = False
if responsable not in EQUIPO:
    aviso_responsable = f"'{responsable}' no es del equipo. Pasa a {COORDINADORA}."
    responsable = COORDINADORA
    requiere_reasignacion = True

# --- 3. Prioridad: dominio, interpretando sinonimos ---
prioridad = input("Prioridad (alta/media/baja) : ").strip().lower()
aviso_prioridad = ""
if prioridad in PRIORIDADES:
    pass
elif prioridad in SINONIMOS_ALTA:
    aviso_prioridad = f"'{prioridad}' se interpreta como prioridad alta."
    prioridad = "alta"
else:
    aviso_prioridad = f"'{prioridad}' no es una prioridad. Se asigna 'media'."
    prioridad = "media"

# --- 4. Dias estimados: tipo y coherencia, ajustando al rango ---
dias_texto = input("Dias estimados              : ").strip()
aviso_dias = ""
if not dias_texto.isdigit():
    aviso_dias = f"'{dias_texto}' no es un numero. Se asignan {DIAS_POR_DEFECTO} dias."
    dias_estimados = DIAS_POR_DEFECTO
else:
    dias_estimados = int(dias_texto)
    if dias_estimados < DIAS_MIN:
        aviso_dias = f"{dias_estimados} dias es demasiado poco. Se ajusta a {DIAS_MIN}."
        dias_estimados = DIAS_MIN
    elif dias_estimados > DIAS_MAX:
        aviso_dias = f"{dias_estimados} dias excede el maximo. Se ajusta a {DIAS_MAX}."
        dias_estimados = DIAS_MAX

# --- 5. Proceso: clasificacion y salida ---
horas_estimadas = dias_estimados * HORAS_POR_JORNADA
completada = False
if prioridad == "alta" and not completada:
    clasificacion = "URGENTE"
elif prioridad == "media" or dias_estimados <= 3:
    clasificacion = "NORMAL"
else:
    clasificacion = "APLAZABLE"
estado = "Completada" if completada else "Pendiente"
print("=" * ANCHO)
print(f"{'Titulo':<16}{titulo:>{ANCHO - 16}}")
print(f"{'Responsable':<16}{responsable:>{ANCHO - 16}}")
print(f"{'Prioridad':<16}{prioridad:>{ANCHO - 16}}")
print(f"{'Dias / horas':<16}{f'{dias_estimados} / {horas_estimadas}':>{ANCHO - 16}}")
print(f"{'Estado':<16}{f'{estado}, {clasificacion}':>{ANCHO - 16}}")
print("=" * ANCHO)
if requiere_reasignacion:
    print(f">> {COORDINADORA} debe reasignar esta tarea.")
if aviso_responsable or aviso_prioridad or aviso_dias:
    print("\nIncidencias detectadas:")
    if aviso_responsable:
        print(" -", aviso_responsable)
    if aviso_prioridad:
        print(" -", aviso_prioridad)
    if aviso_dias:
        print(" -", aviso_dias)

Con las entradas Cartel feria del libro, Pedro, URGENTISIMA y 800, la salida es:

==============================================
Titulo                   Cartel feria del libro
Responsable                              Marta
Prioridad                                 alta
Dias / horas                         365 / 2920
Estado                     Pendiente, URGENTE
==============================================
>> Marta debe reasignar esta tarea.

Incidencias detectadas:
 - 'Pedro' no es del equipo. Pasa a Marta.
 - 'urgentisima' se interpreta como prioridad alta.
 - 800 dias excede el maximo. Se ajusta a 365.

En la versión 0.5, urgentisima se convertía en media (perdiendo la intención del usuario) y 800 en 5 (un número inventado). Ahora cada corrección conserva la información del dato original. Eso es decidir.

El pass de la primera rama es una instrucción que no hace nada, y existe porque Python exige que todo bloque tenga al menos una instrucción: es la forma de decir «aquí no hay que hacer nada, y es intencionado». Y la clasificación usa elif prioridad == "media" or dias_estimados <= 3 porque una tarea de prioridad baja pero de tres días o menos conviene despacharla, no aplazarla: las reglas de negocio reales tienen esta forma. Aun así, el cabo suelto queda más visible que nunca: cuando el dato es irreconocible, lo suyo sería volver a preguntar, no inventar un valor. Para eso hace falta repetir.

Errores Comunes y Consejos

Olvidar los dos puntos. if dias > 5 sin : da SyntaxError: expected ':'. Pasa igual en if, elif y else. Y else prioridad == "baja": tampoco existe: el else nunca lleva condición; si la necesitas, es un elif.

Confundir = con ==. if prioridad = "alta": es un SyntaxError. Uno asigna, el otro compara; Python te protege aquí, pero otros lenguajes no.

Encadenar if cuando querías elif. El error del apartado 5, y el peor de todos porque no da error: simplemente el resultado es otro.

Dejar código muerto por el orden de las condiciones. if dias > 5 antes de elif dias > 15 hace inalcanzable la segunda rama. Ordena de lo más restrictivo a lo más general. Y escribir if prioridad == "alta" or "media" es siempre verdadero: usa in ("alta", "media").

Consejo: pon siempre un else final en las cadenas de elif. El día que aparezca un valor imprevisto, el else te lo dirá en vez de dejar una variable sin definir.

Consejo: extrae las condiciones largas a una variable con nombre. es_urgente = prioridad == "alta" and not completada, y luego if es_urgente:. El código se lee como una frase y la condición se puede imprimir para depurarla.

Ejercicios

Ejercicio 1: Predecir la salida

Sin ejecutar nada, indica qué imprime cada fragmento.

# a)
dias = 20
if dias > 15:
    print("muy larga")
elif dias > 5:
    print("larga")
else:
    print("corta")

# b)
dias = 20
if dias > 15:
    print("muy larga")
if dias > 5:
    print("larga")
else:
    print("corta")

Ejercicio 2: Clasificador de carga de trabajo

Escribe carga_trabajo.py que pida el nombre de una persona del equipo y las horas asignadas esta semana, y que:

  1. Normalice y valide el nombre contra EQUIPO; si no pertenece, avise y lo asigne a "Marta". Valide con .isdigit() que las horas son un entero entre 0 y 60; si no, avise y asigne 40.
  2. Clasifique la carga con if / elif / else: "Infrautilizado" por debajo de 20, "Correcto" de 20 a 40, "Sobrecargado" de 41 a 50 y "Riesgo de burnout" por encima.
  3. Calcule con un ternario el texto "jornada completa" o "jornada parcial" según si llega a 35 horas.
  4. Si está sobrecargada y además es Marta, imprima un aviso extra: la coordinadora no puede ser el cuello de botella. Muestre por último una ficha alineada, y las incidencias solo si las hay.

Ejercicio 3: Aplanar un anidamiento

Reescribe sin ningún if anidado, con el mismo comportamiento, esta estructura: un if titulo != "": contiene un if responsable in EQUIPO:, que a su vez contiene un if dias >= 1: con el mensaje "Tarea valida"; los tres else imprimen, de dentro afuera, "Dias incorrectos", "Responsable incorrecto" y "Falta el titulo".

Soluciones

Solución 1.

Salida Por qué
a muy larga Las dos primeras condiciones son ciertas, pero elif corta tras la primera
b muy larga y larga Son dos if independientes y ambas condiciones son ciertas; el else pertenece al segundo

Es el mismo código salvo por tres letras, y da salidas distintas: dos líneas donde el programador esperaba una, sin ningún error que lo delate.

Solución 2.

# carga_trabajo.py - Estudio Alba
ANCHO = 42
EQUIPO = ("Marta", "Luis", "Nuria")
COORDINADORA = "Marta"
HORAS_POR_DEFECTO = 40
nombre = input("Persona (Marta/Luis/Nuria): ").strip().capitalize()
aviso_nombre = ""
if nombre not in EQUIPO:
    aviso_nombre = f"'{nombre}' no es del equipo. Se asigna a {COORDINADORA}."
    nombre = COORDINADORA

horas_texto = input("Horas asignadas (0-60)    : ").strip()
aviso_horas = ""
if horas_texto.isdigit() and 0 <= int(horas_texto) <= 60:
    horas = int(horas_texto)
else:
    aviso_horas = f"'{horas_texto}' no es valido. Se asignan {HORAS_POR_DEFECTO} h."
    horas = HORAS_POR_DEFECTO

if horas < 20:
    clasificacion = "Infrautilizado"
elif horas <= 40:
    clasificacion = "Correcto"
elif horas <= 50:
    clasificacion = "Sobrecargado"
else:
    clasificacion = "Riesgo de burnout"

jornada = "jornada completa" if horas >= 35 else "jornada parcial"

print(f"{nombre:<18}{f'{horas} h, {jornada}':>{ANCHO - 18}}")
print(f"{'Clasificacion':<18}{clasificacion:>{ANCHO - 18}}")

if horas > 40 and nombre == COORDINADORA:
    print(">> La coordinadora esta sobrecargada: repartir trabajo.")
if aviso_nombre or aviso_horas:
    print(aviso_nombre, aviso_horas)

Dos detalles. La validación de las horas aprovecha el cortocircuito: int(horas_texto) solo se ejecuta si .isdigit() ya ha dicho que es convertible, así que una sola condición cubre el tipo y el rango. Y en la cadena de elif, como cada rama solo se evalúa si la anterior falló, al llegar a elif horas <= 40 ya sabemos que horas >= 20: las condiciones de un elif arrastran implícitamente la negación de las anteriores, y aprovecharlo simplifica mucho el código.

Solución 3.

if titulo == "":
    print("Falta el titulo")
elif responsable not in EQUIPO:
    print("Responsable incorrecto")
elif dias < 1:
    print("Dias incorrectos")
else:
    print("Tarea valida")

Esta forma se llama cláusulas de guarda: primero los casos de error, cada uno con su mensaje, y el caso correcto al final sin sangría acumulada. Añadir una validación nueva es añadir un elif, no abrir otro nivel. Y el orden de lectura coincide con el orden de las comprobaciones, algo que en la versión anidada se perdía: al leer "Dias incorrectos" allí dentro no era evidente que el título y el responsable ya eran correctos.

Conclusión

Tu programa ya sabe elegir. Sabes escribir un if con su condición, sus dos puntos y su bloque indentado de 4 espacios, y entiendes que en Python la indentación es gramática: IndentationError es el precio de escribirla mal. Sabes añadir un else, encadenar elif para alternativas excluyentes y —lo más importante— distinguir cuándo un problema pide elif y cuándo pide varios if independientes, porque esa confusión no produce error sino resultados incorrectos. Sabes combinar condiciones con and, or y not aprovechando el cortocircuito para protegerte de un ValueError, anidar cuando la pregunta interior depende de la exterior y aplanar con cláusulas de guarda cuando no, elegir un valor en una línea con el ternario y comprobar rangos con 1 <= dias <= 365.

TareaFácil ha pasado a la versión 0.5.1 y ha cambiado de actitud: ya no sustituye en silencio los datos raros, sino que interpreta lo que puede, ajusta lo que se sale de rango, reasigna con criterio a la coordinadora y clasifica el trabajo en urgente, normal o aplazable. Los avisos vacíos han desaparecido, tal como prometimos al cerrar el módulo 2. Pero queda una decisión que el programa sigue sin poder tomar, y es la más natural: cuando Marta escribe una prioridad que no existe, lo lógico no es corregirla ni inventarla, sino pedírsela otra vez. Con un if es imposible: se ejecuta una vez y sigue adelante. Tampoco podemos registrar una segunda tarea sin relanzar el programa, ni sumar los días de las cinco tareas que Marta tiene en la cabeza. Todo eso es lo mismo: repetir. Es lo que aprenderás en Bucles, donde while y for cerrarán por fin el cabo suelto de volver a preguntar hasta que el dato sea válido.

Fundamentos de la Programación

Módulo 1: Introducción a la Programación

Módulo 2: Conceptos Básicos

Módulo 3: Estructuras de Control

Módulo 4: Funciones y Procedimientos

Módulo 5: Estructuras de Datos

Módulo 6: Algoritmos Básicos

Módulo 7: Objetos y Organización del Código

Módulo 8: Buenas Prácticas y Herramientas

Módulo 9: Proyecto Final y Cierre del Curso

© Copyright 2026. Todos los derechos reservados