En esta página

Operadores y control de flujo

14 min lectura TextoCap. 1 — Fundamentos de Python

Operadores en Python

Los operadores son los símbolos que permiten realizar operaciones sobre valores. Python tiene varios grupos de operadores, cada uno con su propósito específico.

Operadores aritméticos

a = 17
b = 5

print(a + b)   # 22 — suma
print(a - b)   # 12 — resta
print(a * b)   # 85 — multiplicación
print(a / b)   # 3.4 — división (siempre devuelve float)
print(a // b)  # 3  — división entera (floor division)
print(a % b)   # 2  — módulo (resto)
print(a ** b)  # 1419857 — potencia (17 elevado a 5)

# División entera y módulo trabajan juntos
cociente = a // b   # 3
resto = a % b       # 2
print(f"{a} = {b} × {cociente} + {resto}")  # 17 = 5 × 3 + 2

# El operador % también es útil para verificar paridad
for n in range(10):
    if n % 2 == 0:
        print(f"{n} es par")
    else:
        print(f"{n} es impar")

Operadores de comparación

Los operadores de comparación devuelven valores booleanos (True o False):

x = 10
y = 20

print(x == y)   # False — igual a
print(x != y)   # True  — diferente de
print(x < y)    # True  — menor que
print(x > y)    # False — mayor que
print(x <= y)   # True  — menor o igual que
print(x >= y)   # False — mayor o igual que

# Python permite encadenar comparaciones (muy Pythónico)
edad = 25
print(18 <= edad < 65)      # True — adulto en edad laboral
print(0 <= x <= 100)         # True — x está en rango [0, 100]

# Comparación de strings (lexicográfica)
print("abc" < "abd")   # True
print("ABC" < "abc")   # True (mayúsculas tienen menor valor ASCII)
print("manzana" == "manzana")  # True

Operadores lógicos

llueve = True
tengo_paraguas = False
hace_frio = True

# and — ambas condiciones deben ser True
puedo_salir_seco = llueve and tengo_paraguas
print(puedo_salir_seco)  # False

# or — al menos una condición debe ser True
necesito_abrigo = llueve or hace_frio
print(necesito_abrigo)  # True

# not — invierte el valor booleano
print(not llueve)        # False
print(not tengo_paraguas) # True

# Cortocircuito (short-circuit evaluation)
# 'and' detiene la evaluación en el primer False
# 'or' detiene la evaluación en el primer True
x = None
# Sin cortocircuito, esto lanzaría AttributeError
resultado = x is not None and x.upper()
print(resultado)  # False — se detiene antes de evaluar x.upper()

# 'or' para valores por defecto
nombre_usuario = ""
nombre_mostrar = nombre_usuario or "Anónimo"
print(nombre_mostrar)  # "Anónimo"

Operadores de asignación

contador = 10

contador += 5   # contador = contador + 5  → 15
contador -= 3   # contador = contador - 3  → 12
contador *= 2   # contador = contador * 2  → 24
contador //= 5  # contador = contador // 5 → 4
contador **= 3  # contador = contador ** 3 → 64
contador %= 10  # contador = contador % 10 → 4

# Operador morsa (walrus operator, Python 3.8+)
# Asigna y evalúa en una sola expresión
import random

# En lugar de esto:
n = random.randint(1, 100)
if n > 50:
    print(f"{n} es mayor que 50")

# Puedes escribir esto:
if (n := random.randint(1, 100)) > 50:
    print(f"{n} es mayor que 50")

Operadores de pertenencia e identidad

frutas = ["manzana", "pera", "uva"]

# in — comprueba pertenencia
print("pera" in frutas)         # True
print("sandía" in frutas)       # False
print("sandía" not in frutas)   # True

# in también funciona con strings
print("ython" in "Python")      # True
print("java" in "Python")       # False

# is — comprueba identidad de objeto (misma referencia en memoria)
a = [1, 2, 3]
b = [1, 2, 3]
c = a

print(a == b)   # True  — mismos valores
print(a is b)   # False — objetos distintos en memoria
print(a is c)   # True  — misma referencia

# Solo usa 'is' con None, True, False (singletons)
x = None
print(x is None)     # Correcto
print(x == None)     # Funciona, pero es menos Pythónico

Control de flujo: if / elif / else

La estructura if en Python usa indentación (4 espacios es el estándar) en lugar de llaves:

def clasificar_temperatura(celsius: float) -> str:
    if celsius < 0:
        return "Bajo cero — ¡congelando!"
    elif celsius < 10:
        return "Muy frío"
    elif celsius < 20:
        return "Frío"
    elif celsius < 27:
        return "Templado — perfecto"
    elif celsius < 35:
        return "Cálido"
    else:
        return "Caliente — ¡busca sombra!"

print(clasificar_temperatura(-5))   # Bajo cero — ¡congelando!
print(clasificar_temperatura(22))   # Templado — perfecto
print(clasificar_temperatura(38))   # Caliente — ¡busca sombra!

Expresión ternaria (if en una línea)

edad = 20
estado = "mayor de edad" if edad >= 18 else "menor de edad"
print(estado)  # mayor de edad

# Anidadas (úsalas con moderación — pueden reducir legibilidad)
nota = 75
letra = "A" if nota >= 90 else "B" if nota >= 80 else "C" if nota >= 70 else "F"
print(letra)  # C

match / case — Coincidencia estructural de patrones (Python 3.10+)

El match/case es mucho más poderoso que un simple switch. Puede hacer coincidencia por tipo, por estructura de datos y capturar valores:

# Coincidencia simple con valores
def dia_semana(numero: int) -> str:
    match numero:
        case 1:
            return "Lunes"
        case 2:
            return "Martes"
        case 3:
            return "Miércoles"
        case 4:
            return "Jueves"
        case 5:
            return "Viernes"
        case 6 | 7:          # Alternativas con |
            return "Fin de semana"
        case _:              # Caso por defecto (wildcard)
            return "Número inválido"


# Coincidencia con tipos y estructuras
def procesar_evento(evento: dict) -> str:
    match evento:
        case {"tipo": "clic", "x": x, "y": y}:
            return f"Clic en ({x}, {y})"
        case {"tipo": "tecla", "codigo": codigo}:
            return f"Tecla presionada: {codigo}"
        case {"tipo": "scroll", "delta": delta} if delta > 0:
            return "Desplazamiento hacia arriba"
        case {"tipo": "scroll"}:
            return "Desplazamiento hacia abajo"
        case _:
            return "Evento desconocido"

print(procesar_evento({"tipo": "clic", "x": 100, "y": 200}))
# Clic en (100, 200)
print(procesar_evento({"tipo": "scroll", "delta": -3}))
# Desplazamiento hacia abajo


# Coincidencia con estructuras de datos (listas, tuplas)
def describir_punto(punto: tuple) -> str:
    match punto:
        case (0, 0):
            return "Origen"
        case (x, 0):
            return f"En el eje X: {x}"
        case (0, y):
            return f"En el eje Y: {y}"
        case (x, y):
            return f"Punto ({x}, {y})"

print(describir_punto((0, 0)))    # Origen
print(describir_punto((5, 0)))    # En el eje X: 5
print(describir_punto((3, 7)))    # Punto (3, 7)

Bucle for

El bucle for en Python itera sobre cualquier objeto iterable: listas, cadenas, rangos, diccionarios, etc.

# Iterar sobre una lista
frutas = ["manzana", "pera", "uva", "naranja"]
for fruta in frutas:
    print(f"Fruta: {fruta}")

# Iterar sobre una cadena
for letra in "Python":
    print(letra, end=" ")  # P y t h o n
print()

# range() — genera secuencias de números
for i in range(5):          # 0, 1, 2, 3, 4
    print(i)

for i in range(1, 6):       # 1, 2, 3, 4, 5
    print(i)

for i in range(0, 10, 2):   # 0, 2, 4, 6, 8 (paso de 2)
    print(i)

for i in range(10, 0, -1):  # 10, 9, 8, ..., 1 (decreciente)
    print(i)

# enumerate() — índice + valor simultáneamente
for indice, fruta in enumerate(frutas):
    print(f"{indice + 1}. {fruta}")
# 1. manzana
# 2. pera
# 3. uva
# 4. naranja

# enumerate() con inicio personalizado
for indice, fruta in enumerate(frutas, start=1):
    print(f"{indice}. {fruta}")

# zip() — iterar dos listas en paralelo
nombres = ["Ana", "Bruno", "Carmen"]
edades = [25, 30, 28]
for nombre, edad in zip(nombres, edades):
    print(f"{nombre} tiene {edad} años")

Bucle while

El bucle while continúa mientras una condición sea True:

# Cuenta regresiva
contador = 5
while contador > 0:
    print(f"T-{contador}...")
    contador -= 1
print("¡Despegue!")

# Lectura con validación (patrón muy común)
def pedir_entero(mensaje: str, minimo: int, maximo: int) -> int:
    while True:
        try:
            valor = int(input(mensaje))
            if minimo <= valor <= maximo:
                return valor
            print(f"Por favor ingresa un número entre {minimo} y {maximo}")
        except ValueError:
            print("Eso no es un número entero válido")

# Número secreto (ejemplo de while con lógica de juego)
import random

secreto = random.randint(1, 100)
intentos = 0

while True:
    intentos += 1
    # En un programa real, usarías input()
    intento = secreto  # Simulamos la respuesta correcta

    if intento < secreto:
        print("¡Más alto!")
    elif intento > secreto:
        print("¡Más bajo!")
    else:
        print(f"¡Correcto! Lo lograste en {intentos} intentos")
        break

break, continue y else en bucles

Python tiene una característica única: los bucles for y while pueden tener un bloque else:

# break — sale del bucle inmediatamente
numeros = [1, 3, 5, 8, 11, 13]
for num in numeros:
    if num % 2 == 0:
        print(f"Primer par encontrado: {num}")
        break
else:
    # Este bloque se ejecuta SOLO si el bucle terminó sin 'break'
    print("No se encontraron números pares")

# continue — salta a la siguiente iteración
print("Números impares del 1 al 10:")
for n in range(1, 11):
    if n % 2 == 0:
        continue  # Salta los pares
    print(n, end=" ")
print()

# El 'else' del for es muy útil para búsquedas
def buscar_primo(numeros: list[int]) -> int | None:
    for n in numeros:
        if n > 1:
            for i in range(2, int(n**0.5) + 1):
                if n % i == 0:
                    break
            else:
                # El for interno terminó sin break — n es primo
                return n
    return None

candidatos = [4, 6, 9, 15, 17, 20]
primo = buscar_primo(candidatos)
print(f"Primer primo encontrado: {primo}")  # 17

Resumen

Dominas ahora el arsenal de operadores de Python y las estructuras de control de flujo fundamentales. El match/case de Python 3.10+ es especialmente poderoso para código limpio frente a múltiples condiciones. El par break/else en bucles es una característica única de Python que resuelve patrones de búsqueda de manera elegante. En la próxima lección profundizaremos en las funciones, el scope y las closures.

match/case no es un switch tradicional
El match/case de Python 3.10+ es coincidencia estructural de patrones (structural pattern matching). Puede hacer coincidir tipos, estructuras de datos, condiciones adicionales (guards) y capturar valores simultáneamente.
Cuidado con el operador 'is' vs '=='
Usa '==' para comparar valores y 'is' para comparar identidad de objeto (si son el mismo objeto en memoria). Solo usa 'is' con None, True y False.
range() es perezoso (lazy)
range(1_000_000) no crea una lista de un millón de elementos en memoria. Genera cada número cuando se necesita. Es mucho más eficiente que crear una lista con list(range(1_000_000)).