En la lección anterior conseguiste que los datos de una tarea de Estudio Alba vivieran en variables. El programa ya guarda información, pero todavía no hace nada con ella: no sabe decir cuántos días faltan para la entrega del logotipo de la Panadería Solé, ni si esa tarea es urgente, ni qué porcentaje del encargo lleva completado Luis.

Ese salto —de guardar a calcular— lo dan los operadores. Un operador es un símbolo que combina valores para producir uno nuevo: + suma, > compara, and conjuga condiciones. Y la combinación de valores, variables y operadores se llama expresión. Todo programa, por complejo que sea, es en el fondo una acumulación de expresiones que se evalúan y de valores que se asignan.

En esta lección aprenderás qué es una expresión y en qué orden se evalúa; los operadores aritméticos, con especial atención a esos dos que suelen ignorarse y resultan utilísimos (// y %); la asignación compuesta (+= y compañía); los operadores de comparación, que siempre producen un booleano; los operadores lógicos and, or y not con su tabla de verdad y su evaluación perezosa; el operador in de pertenencia; la diferencia entre == e is; y cómo funcionan + y * con textos. Cerraremos con la precedencia y con la aplicación de todo ello a TareaFácil.

Contenido

  1. Qué es una expresión y cómo se evalúa
  2. Operadores aritméticos
  3. División entera // y módulo %: los dos infravalorados
  4. Asignación compuesta
  5. Operadores de comparación
  6. Operadores lógicos y tabla de verdad
  7. Evaluación perezosa (cortocircuito)
  8. El operador de pertenencia in
  9. == frente a is
  10. Operadores con cadenas: + y *
  11. Precedencia de operadores
  12. TareaFácil: calcular sobre los datos de la tarea
  13. Errores comunes y consejos
  14. Ejercicios
  15. Conclusión

  1. Qué es una expresión y cómo se evalúa

Una expresión es cualquier combinación de valores, variables y operadores que Python puede evaluar hasta reducirla a un único valor.

Estas son todas expresiones:

7
7 + 3
dias_estimados * 8
horas_dedicadas > 5
prioridad == "alta"

Y esta no lo es: dias = 3. Es una instrucción (una asignación), no una expresión. La distinción es fina pero clarificadora:

Expresión Instrucción
Qué hace Se reduce a un valor Realiza una acción
Ejemplo 7 + 3 → 10 total = 7 + 3
¿Se puede poner a la derecha de un =? Sí No

Fíjate en que una asignación contiene una expresión: en total = 7 + 3, la parte derecha es la expresión y el = es la acción de guardar su resultado.

Evaluar significa ir sustituyendo cada pieza por su valor hasta que solo queda uno. Con este código:

dias_estimados = 3
horas_por_dia = 8

horas_totales = dias_estimados * horas_por_dia
print(horas_totales)      # 24

Python hace lo siguiente al llegar a la tercera línea:

dias_estimados * horas_por_dia
        3       *       8
                24

Primero sustituye cada nombre por el valor al que apunta, después aplica el operador, y por último asigna el resultado. Ese orden —valores primero, operación después, asignación al final— es el que debes tener en la cabeza al leer cualquier línea de código.

Los valores sobre los que actúa un operador se llaman operandos. En 3 * 8, el operador es * y los operandos son 3 y 8.

  1. Operadores aritméticos

Python ofrece siete operadores aritméticos. Los tres primeros no te sorprenderán; los cuatro siguientes merecen atención.

Operador Nombre Ejemplo Resultado Tipo del resultado
+ Suma 7 + 3 10 int
- Resta 7 - 3 4 int
* Multiplicación 7 * 3 21 int
/ División real 7 / 3 2.3333333333333335 float siempre
// División entera 7 // 3 2 int (si ambos son int)
% Módulo (resto) 7 % 3 1 int
** Potencia 7 ** 3 343 int

Veámoslos funcionando:

print(7 + 3)      # 10
print(7 - 3)      # 4
print(7 * 3)      # 21
print(7 / 3)      # 2.3333333333333335
print(7 // 3)     # 2
print(7 % 3)      # 1
print(7 ** 3)     # 343

Hay una trampa clásica en la cuarta línea. / devuelve siempre un float, aunque la división sea exacta:

print(6 / 3)          # 2.0   <- no 2
print(type(6 / 3))    # <class 'float'>
print(6 // 3)         # 2
print(type(6 // 3))   # <class 'int'>

Si necesitas un número entero —para contar cosas, por ejemplo— usa //, no /. Un contador de tareas que vale 2.0 en lugar de 2 produce salidas feas y comparaciones confusas.

Otro detalle: si mezclas un int y un float en cualquier operación, el resultado es float. Es la conversión implícita, y la estudiaremos con más detalle en Conversión de tipos y validación:

print(3 + 1.5)          # 4.5
print(type(3 + 1.5))    # <class 'float'>

Los signos negativos funcionan como esperas, y el - también puede usarse con un solo operando (se llama menos unario):

diferencia = 3 - 5
print(diferencia)     # -2
print(-diferencia)    # 2

Una advertencia sobre / y //: dividir por cero es un error. 7 / 0 detiene el programa con ZeroDivisionError: division by zero. Cuando el divisor venga de un dato variable (por ejemplo, el número de tareas de una persona, que puede ser cero), habrá que comprobarlo antes. Hacer esa comprobación requiere condicionales, que llegan en Condicionales.

  1. División entera // y módulo %: los dos infravalorados

Estos dos operadores se suelen mirar por encima y luego resultan ser los que más se usan en problemas reales. Merecen su propio apartado.

Piensa en la división de toda la vida, la del colegio: 17 entre 5 da 3 de cociente y 2 de resto. Pues bien:

  • // te da el cociente entero: 17 // 5 → 3
  • % te da el resto: 17 % 5 → 2
minutos_totales = 195

horas = minutos_totales // 60     # 3
minutos = minutos_totales % 60    # 15

print(horas)      # 3
print(minutos)    # 15

195 minutos son 3 horas y 15 minutos. Esta pareja de operadores es la forma estándar de descomponer una cantidad en unidades mayores y menores: minutos en horas, días en semanas, céntimos en euros.

Aplicado a Estudio Alba: Luis lleva 27 horas dedicadas a un encargo y trabaja jornadas de 8 horas. ¿Cuántas jornadas completas ha invertido y cuántas horas sueltas van de la siguiente?

HORAS_POR_JORNADA = 8
horas_dedicadas = 27

jornadas_completas = horas_dedicadas // HORAS_POR_JORNADA    # 3
horas_sueltas = horas_dedicadas % HORAS_POR_JORNADA          # 3

print(jornadas_completas)   # 3
print(horas_sueltas)        # 3

El otro gran uso de % es detectar múltiplos. Un número es múltiplo de otro cuando el resto de dividirlos es cero:

numero_tarea = 12

print(numero_tarea % 2)     # 0  -> es par
print(numero_tarea % 5)     # 2  -> no es multiplo de 5

n % 2 == 0 significa «n es par». Es una construcción que verás mil veces.

Necesidad Operador Ejemplo
Repartir en grupos completos // 27 // 8 → 3 jornadas enteras
Saber cuánto sobra del reparto % 27 % 8 → 3 horas sueltas
Comprobar si es múltiplo / par % 12 % 2 → 0, luego es par
Obtener el último dígito % 1250 % 10 → 0
Valor exacto con decimales / 27 / 8 → 3.375 jornadas

Un aviso sobre negativos, para que no te pille por sorpresa: -7 // 2 da -4, no -3, porque Python redondea hacia abajo, no hacia cero. Y -7 % 2 da 1, no -1. Con cantidades positivas —el 99 % de los casos— no notarás la diferencia.

  1. Asignación compuesta

Aumentar el valor de una variable es tan frecuente que Python ofrece una forma abreviada. Estas dos líneas hacen exactamente lo mismo:

horas_dedicadas = horas_dedicadas + 2
horas_dedicadas += 2

La segunda se lee «suma 2 a horas_dedicadas». Existe una versión abreviada para cada operador aritmético:

Abreviatura Equivale a Ejemplo (partiendo de x = 10) Resultado
x += 3 x = x + 3 13
x -= 3 x = x - 3 7
x *= 3 x = x * 3 30
x /= 4 x = x / 4 2.5 (float)
x //= 3 x = x // 3 3
x %= 3 x = x % 3 1
x **= 2 x = x ** 2 100

Un ejemplo con el hilo del curso: Luis va anotando las horas que dedica al logotipo a lo largo de la semana.

horas_dedicadas = 0

horas_dedicadas += 3.5      # lunes
horas_dedicadas += 2        # martes
horas_dedicadas += 4        # jueves

print(horas_dedicadas)      # 9.5

Fíjate en un detalle imprescindible: la variable debe existir antes. horas_dedicadas += 3.5 es horas_dedicadas = horas_dedicadas + 3.5, y para calcular la parte derecha Python necesita saber cuánto vale ahora. Si no se ha asignado nunca, obtendrás NameError. Por eso la primera línea la inicializa a 0.

También funciona con textos, porque + concatena cadenas (lo veremos en el apartado 10):

resumen = "Tarea: "
resumen += "logotipo"
print(resumen)              # Tarea: logotipo

  1. Operadores de comparación

Los operadores de comparación responden a preguntas sobre dos valores. Y lo hacen de una manera muy concreta: devuelven siempre un valor booleano, True o False. Nunca otra cosa.

Operador Significado Ejemplo Resultado
== ¿Son iguales? 3 == 3 True
!= ¿Son distintos? 3 != 5 True
> ¿Mayor que? 3 > 5 False
< ¿Menor que? 3 < 5 True
>= ¿Mayor o igual? 5 >= 5 True
<= ¿Menor o igual? 3 <= 2 False
dias_estimados = 3
dias_transcurridos = 5

print(dias_transcurridos > dias_estimados)    # True  -> hay retraso
print(dias_estimados == dias_transcurridos)   # False
print(dias_estimados != dias_transcurridos)   # True

Ese True que ves impreso no es un texto decorativo: es un valor de tipo bool, y como tal se puede guardar en una variable y usarse después.

hay_retraso = dias_transcurridos > dias_estimados
print(hay_retraso)          # True
print(type(hay_retraso))    # <class 'bool'>

Esta es una idea potente y conviene subrayarla: una comparación produce un dato como cualquier otro. No es una pregunta que se hace y se olvida; es un valor que puedes almacenar, nombrar y reutilizar. Cuando lleguen los condicionales en el módulo 3, ese valor será el que decida qué camino toma el programa.

Las comparaciones también funcionan con textos, y ahí hay dos cosas que saber:

print("alta" == "alta")     # True
print("alta" == "ALTA")     # False  <- distinguen mayusculas
print("alta" < "media")     # True   <- orden alfabetico

La segunda línea es exactamente el problema que descubrimos con la prueba de escritorio de la lección 01-05: "ALTA" y "alta" son textos distintos para Python. La solución llega en Conversión de tipos y validación.

La tercera muestra que < y > comparan textos alfabéticamente, carácter a carácter según su código interno. Cuidado: como las mayúsculas van antes que las minúsculas en ese orden, "Zorro" < "alba" es True. Es una fuente de sorpresas al ordenar nombres.

Python permite además encadenar comparaciones, algo poco habitual en otros lenguajes y muy legible:

dias = 5
print(1 <= dias <= 7)       # True  -> esta dentro de la semana

Eso se lee tal cual: «días está entre 1 y 7, ambos incluidos».

  1. Operadores lógicos y tabla de verdad

Los operadores lógicos combinan valores booleanos para construir condiciones compuestas. Son tres:

Operador Significado Devuelve True cuando...
and Y Ambos operandos son verdaderos
or O Al menos uno es verdadero
not No Invierte el valor del operando

En Python se escriben con palabras en inglés, no con símbolos como && o || de otros lenguajes.

Tabla de verdad de and:

A B A and B
True True True
True False False
False True False
False False False

Tabla de verdad de or:

A B A or B
True True True
True False True
False True True
False False False

Tabla de verdad de not:

A not A
True False
False True

Y ahora con datos de Estudio Alba:

prioridad = "alta"
completada = False

es_urgente = prioridad == "alta" and not completada
print(es_urgente)           # True

Desmenucemos esa expresión, porque condensa casi todo lo visto hasta ahora:

prioridad == "alta"  and  not completada
      True           and  not False
      True           and     True
                  True
  1. prioridad == "alta" es una comparación → True.
  2. completada vale False, así que not completada → True.
  3. True and True → True.

Se lee en español casi literalmente: «es urgente si la prioridad es alta y no está completada». Cuando una expresión lógica se lee bien en voz alta, suele estar bien escrita.

Ahora al revés, con la tarea ya terminada:

prioridad = "alta"
completada = True

es_urgente = prioridad == "alta" and not completada
print(es_urgente)           # False

not True es False, y True and False es False. La tarea es de prioridad alta pero ya está hecha: no es urgente. Correcto.

Un ejemplo con or, para decidir si una tarea necesita revisión de Marta:

prioridad = "media"
horas_dedicadas = 22
HORAS_LIMITE = 20

necesita_revision = prioridad == "alta" or horas_dedicadas > HORAS_LIMITE
print(necesita_revision)    # True

No es de prioridad alta (False), pero se ha pasado del límite de horas (True). Con or basta con que se cumpla una: True.

Ten cuidado con un error frecuentísimo al traducir del español. Esto no funciona como parece:

prioridad = "baja"

# INCORRECTO
es_relevante = prioridad == "alta" or "media"
print(es_relevante)         # media  <- ni siquiera es un booleano

En castellano decimos «la prioridad es alta o media», pero Python no lee así: evalúa prioridad == "alta" (que da False) y luego evalúa "media" por su cuenta, como valor independiente. Un texto no vacío cuenta como verdadero, así que el or devuelve "media" y la expresión resulta verdadera siempre, incluso con una prioridad baja. La forma correcta repite la comparación completa:

# CORRECTO
es_relevante = prioridad == "alta" or prioridad == "media"
print(es_relevante)         # False

O, mejor todavía, usa el operador in que veremos en el apartado 8.

  1. Evaluación perezosa (cortocircuito)

Python evalúa las expresiones lógicas de izquierda a derecha y se detiene en cuanto conoce el resultado. Es lo que se llama evaluación perezosa o cortocircuito.

La lógica es sencilla:

  • En A and B: si A ya es False, el resultado será False pase lo que pase con B. Python ni siquiera evalúa B.
  • En A or B: si A ya es True, el resultado será True. Python no evalúa B.
flowchart TD
    A["Evaluar A and B"] --> B{"A es False?"}
    B -->|Si| C["Resultado False<br/>B no se evalua"]
    B -->|No| D["Evaluar B"]
    D --> E["El resultado es el valor de B"]

¿Por qué importa esto? Por dos motivos muy prácticos.

Primero, por eficiencia. Si la parte derecha es un cálculo costoso, ahorrárselo tiene valor.

Segundo, y mucho más importante, por seguridad. El cortocircuito permite proteger una operación peligrosa poniendo delante la comprobación que la hace segura:

tareas_asignadas = 0
horas_totales = 0

# Si tareas_asignadas es 0, la division nunca se ejecuta
reparto_ok = tareas_asignadas > 0 and horas_totales / tareas_asignadas < 10
print(reparto_ok)      # False, sin ZeroDivisionError

Si Python evaluara ambos lados siempre, la división por cero rompería el programa. Gracias al cortocircuito, al ser tareas_asignadas > 0 igual a False, la parte derecha ni se mira. El orden de los operandos en un and no es indiferente: primero la comprobación que protege, después la operación protegida.

Un matiz curioso que conviene conocer: en Python, and y or no devuelven siempre True o False, sino el operando en el que se detuvieron.

print(0 or "sin asignar")       # sin asignar
print("Luis" or "sin asignar")  # Luis

Como 0 cuenta como falso, or sigue y devuelve el segundo operando. Como "Luis" cuenta como verdadero, se detiene y lo devuelve. Es un idioma habitual para dar valores por defecto. Las reglas exactas de qué valores cuentan como verdaderos las verás en Conversión de tipos y validación.

  1. El operador de pertenencia in

El operador in responde a la pregunta «¿está esto dentro de aquello?» y devuelve un booleano. Sobre textos, comprueba si una cadena aparece dentro de otra:

titulo = "Logotipo Panaderia Sole"

print("Sole" in titulo)          # True
print("Vidal" in titulo)         # False
print("sole" in titulo)          # False  <- distingue mayusculas

Su forma negada es not in:

print("Vidal" not in titulo)     # True

in funciona sobre listas, diccionarios y otras estructuras que verás en el módulo 5. Pero hay un uso que puedes aprovechar desde hoy y que resuelve elegantemente el problema del apartado 6: comprobar si un valor está entre varios posibles.

prioridad = "media"

# Version larga
es_valida = prioridad == "alta" or prioridad == "media" or prioridad == "baja"

# Version corta y clara
es_valida = prioridad in ("alta", "media", "baja")

print(es_valida)      # True

Los paréntesis con valores separados por comas forman una tupla, una colección fija de valores que se estudia en Tuplas y estructuras anidadas. Aquí la usamos solo como lista de opciones válidas: es legible, se amplía añadiendo un valor y evita repetir tres veces el nombre de la variable. Será nuestra herramienta principal para validar la prioridad en la lección 02-04.

Un aviso sobre in en textos: comprueba subcadenas, no palabras completas. "alta" in "prioridad muy alta" es True, pero también lo es "alta" in "cesta alta y baja". Para valores concretos, prefiere in con una tupla de opciones antes que in sobre un texto largo.

  1. == frente a is

Estos dos operadores parecen sinónimos y no lo son. La distinción se entiende bien con el modelo de etiquetas y valores de la lección anterior:

  • == pregunta: ¿valen lo mismo?
  • is pregunta: ¿son exactamente el mismo objeto en memoria?
responsable_a = "Luis"
responsable_b = "Luis"

print(responsable_a == responsable_b)   # True  -> mismo contenido
print(responsable_a is responsable_b)   # True o False, segun optimizaciones internas

Esa segunda línea es justamente el problema: el resultado de is con textos y números depende de detalles internos de Python que no controlas y que pueden cambiar entre versiones. Con textos cortos Python reutiliza el mismo objeto y da True; con textos construidos en tiempo de ejecución puede dar False aunque el contenido sea idéntico.

La regla práctica es simple y no admite excepciones al nivel de este curso:

Quieres saber... Usa Nunca uses
Si dos datos tienen el mismo valor == is
Si una variable es None is None == None

Es decir: usa siempre == para comparar valores, y reserva is para un único caso, comprobar None:

fecha_entrega = None

print(fecha_entrega is None)        # True
print(fecha_entrega is not None)    # False

¿Por qué ahí sí is? Porque None es un valor único en toda la ejecución de Python: solo existe uno. Preguntar si algo «es ese objeto» es exactamente lo que quieres, y es la forma que verás en todo el código profesional.

  1. Operadores con cadenas: + y *

Dos operadores aritméticos tienen un segundo significado cuando trabajan con textos.

+ concatena, es decir, pega dos cadenas en una:

nombre = "Panaderia"
apellido = "Sole"

cliente = nombre + " " + apellido
print(cliente)      # Panaderia Sole

Fíjate en el " " del medio: la concatenación no añade espacios. Sin él obtendrías PanaderiaSole. Es el olvido más común al concatenar.

* repite una cadena un número entero de veces:

separador = "-" * 40
print(separador)    # ----------------------------------------

print("=" * 10)     # ==========

Es la forma habitual de dibujar líneas separadoras sin escribir cuarenta guiones a mano. Lo usaremos en la ficha de TareaFácil.

Y ahora el límite importante. No se puede sumar un texto y un número:

dias = 3
print("Dias: " + dias)
TypeError: can only concatenate str (not "int") to str

El mensaje dice literalmente: «solo se puede concatenar str con str, no con int». Python no adivina lo que quieres. Y hace bien: "3" + 4 podría ser 7 o "34", y la ambigüedad en un lenguaje de programación es peor que un error.

La solución pasa por convertir el número a texto con str(), y eso se ve en Conversión de tipos y validación. En la práctica, para mezclar texto y datos usarás las f-strings de Entrada y salida de datos, que son mucho más cómodas. Por eso en esta lección seguimos imprimiendo cada valor en su propio print.

Operador Con números Con textos
+ Suma: 3 + 4 → 7 Concatena: "a" + "b" → "ab"
* Multiplica: 3 * 4 → 12 Repite: "ab" * 3 → "ababab"
- Resta: 7 - 3 → 4 Error: TypeError
/ Divide Error: TypeError

  1. Precedencia de operadores

Cuando una expresión combina varios operadores, Python aplica un orden fijo llamado precedencia, igual que en matemáticas la multiplicación va antes que la suma.

print(2 + 3 * 4)      # 14, no 20

De mayor a menor prioridad:

Nivel Operadores Ejemplo
1 (más alta) () paréntesis (2 + 3) * 4 → 20
2 ** potencia 2 ** 3 * 4 → 32
3 -x menos unario -2 ** 2 → -4
4 *, /, //, % 2 + 3 * 4 → 14
5 +, -
6 ==, !=, <, >, <=, >=, in, is 2 + 3 > 4 → True
7 not
8 and
9 (más baja) or

Dos consecuencias que conviene retener:

Las comparaciones se evalúan después que la aritmética. Por eso 2 + 3 > 4 funciona: primero suma (5), después compara (5 > 4 → True). No hacen falta paréntesis.

and se evalúa antes que or. Esto sí sorprende:

print(True or False and False)     # True

Se agrupa como True or (False and False) → True or False → True. Si hubieras querido (True or False) and False, el resultado sería False. Aquí los paréntesis no son opcionales: son la diferencia entre dos programas distintos.

Y el consejo práctico, que vale más que la tabla entera: usa paréntesis siempre que la expresión no sea evidente. No cuestan nada, no ralentizan el programa y evitan que quien lea el código —o tú mismo dentro de un mes— tenga que consultar una tabla de precedencias.

# Correcto pero exige conocer la tabla
es_critica = prioridad == "alta" and not completada or horas_dedicadas > 40

# Correcto y evidente
es_critica = (prioridad == "alta" and not completada) or (horas_dedicadas > 40)

Las dos líneas hacen lo mismo. La segunda se entiende sin pensar.

  1. TareaFácil: calcular sobre los datos de la tarea

Aplicamos todo lo aprendido a tareafacil.py. Partimos de las variables de la versión 0.2 y añadimos cálculos: los días que faltan para la entrega, si la tarea es urgente y el porcentaje de avance del encargo de la Panadería Solé.

# tareafacil.py - Estudio Alba
# Version 0.3: calculos sobre los datos de la tarea

NOMBRE_APP = "TareaFacil"
VERSION = "0.3"
HORAS_POR_JORNADA = 8

# --- Datos de la tarea ---
titulo = "Logotipo Panaderia Sole"
responsable = "Luis"
prioridad = "alta"
completada = False

dia_actual = 12               # dia del mes en que estamos
dia_entrega = 19              # dia del mes acordado con el cliente
horas_estimadas = 24          # presupuesto de horas del encargo
horas_dedicadas = 9           # horas que Luis lleva invertidas

print("=" * 40)
print("  TAREAFACIL v0.3 - Estudio Alba")
print("=" * 40)
print(titulo)
print(responsable)
print("-" * 40)

# --- 1. Dias que faltan para la entrega ---
dias_restantes = dia_entrega - dia_actual
print("Dias restantes:")
print(dias_restantes)                       # 7

# --- 2. Jornadas y horas sueltas dedicadas ---
jornadas = horas_dedicadas // HORAS_POR_JORNADA
horas_sueltas = horas_dedicadas % HORAS_POR_JORNADA
print("Jornadas completas dedicadas:")
print(jornadas)                             # 1
print("Horas sueltas:")
print(horas_sueltas)                        # 1

# --- 3. Porcentaje de avance ---
porcentaje_avance = horas_dedicadas / horas_estimadas * 100
print("Porcentaje de avance:")
print(porcentaje_avance)                    # 37.5

# --- 4. Horas que quedan y ritmo necesario ---
horas_restantes = horas_estimadas - horas_dedicadas
print("Horas restantes:")
print(horas_restantes)                      # 15

# --- 5. Es urgente? ---
es_urgente = prioridad == "alta" and not completada
print("Es urgente:")
print(es_urgente)                           # True

# --- 6. Prioridad valida? ---
prioridad_valida = prioridad in ("alta", "media", "baja")
print("Prioridad valida:")
print(prioridad_valida)                     # True

# --- 7. Va con retraso? ---
# Se considera retrasada si ha consumido mas del 60 % del presupuesto
# de horas y aun quedan menos de 5 dias
va_justo = (porcentaje_avance > 60) and (dias_restantes < 5)
print("Va justo de plazo:")
print(va_justo)                             # False

Vamos a comentar los cálculos que tienen enjundia.

Días restantes (dia_entrega - dia_actual). Una resta sencilla que solo funciona dentro del mismo mes; para fechas reales existe el módulo datetime de Python, que queda fuera de este curso introductorio. Lo relevante aquí es que el dato ya no está escrito: se calcula. Si cambias dia_actual a 17, dias_restantes pasa a 2 sin tocar nada más.

Porcentaje de avance (horas_dedicadas / horas_estimadas * 100). Nota que aquí usamos / y no //: queremos el valor exacto con decimales. Con 9 horas de 24, el resultado es 37.5. Y observa la precedencia: / y * tienen el mismo nivel, así que se evalúan de izquierda a derecha: primero 9 / 24 (= 0.375) y luego * 100 (= 37.5). Es lo que queríamos.

Jornadas y horas sueltas (// y %). 9 horas con jornadas de 8 son 1 jornada completa y 1 hora suelta. Es el patrón del apartado 3 aplicado a un dato real.

Es urgente (prioridad == "alta" and not completada). La expresión que resume la regla de negocio de Marta. Es alta y no está hecha: urgente. Prueba a poner completada = True y ejecutar de nuevo: pasa a False.

Va justo de plazo. Aquí los paréntesis no eran necesarios por precedencia (las comparaciones van antes que and), pero se han puesto para que la línea se lea de un vistazo. Ese es exactamente el criterio que debes aplicar.

Prueba de escritorio de la expresión de urgencia

Como en el módulo 1, verifiquemos la lógica con una tabla, sin ejecutar nada:

Caso prioridad completada prioridad == "alta" not completada es_urgente
1 "alta" False True True True
2 "alta" True True False False
3 "media" False False True False
4 "media" True False False False
5 "ALTA" False False True False ← problema

Los cuatro primeros casos son correctos. El quinto vuelve a poner el dedo en la llaga: si Marta escribe la prioridad en mayúsculas, la comparación falla y una tarea urgente pasa desapercibida. No es un fallo de la expresión lógica, sino de los datos que le llegan. Ese es el trabajo de la validación, y lo resolveremos en 02-04.

Errores Comunes y Consejos

Usar = en lugar de == al comparar. prioridad = "alta" asigna; prioridad == "alta" compara. En Python moderno, poner = donde va == dentro de una condición da SyntaxError, lo cual es una suerte: el error salta enseguida.

Esperar un entero de /. 10 / 2 es 5.0, un float. Si cuentas cosas, usa //.

Dividir entre cero. ZeroDivisionError. Cuando el divisor sea un dato variable, protégelo con un and aprovechando el cortocircuito, o con un condicional cuando llegues al módulo 3.

Escribir and, or y not como símbolos. En Python no existen &&, || ni !. Son palabras.

La trampa de x == "alta" or "media". Parece castellano y no lo es. Repite la comparación entera o, mejor, usa x in ("alta", "media").

Usar is para comparar valores. Funciona por casualidad con textos cortos y falla justo cuando el programa ya está en uso. == para valores; is solo con None.

Concatenar texto y número con +. "Dias: " + 3 da TypeError. Espera a las f-strings de la próxima lección: es la solución cómoda.

Olvidar el espacio al concatenar. "Panaderia" + "Sole" produce PanaderiaSole. El espacio hay que ponerlo explícitamente.

Fiarse de la precedencia de memoria. Nadie recuerda si not va antes que and. Pon paréntesis y deja de pensarlo.

Confiar en la comparación exacta de float. 0.1 + 0.2 == 0.3 es False, porque los decimales se almacenan de forma aproximada. Con dinero o medidas, compara con un margen de tolerancia en lugar de exigir igualdad exacta.

Consejo: nombra las expresiones lógicas. En vez de repetir prioridad == "alta" and not completada en varios sitios, guárdala en es_urgente y usa el nombre. El código se lee como una frase y, si la regla cambia, se toca en un solo sitio.

Consejo: usa el REPL como calculadora. Cuando dudes de qué da -7 // 2 o "alta" < "media", no razones: abre el intérprete y pruébalo. Tardas cinco segundos y sales de dudas para siempre.

Ejercicios

Ejercicio 1: Evaluar expresiones a mano

Escribe el resultado y el tipo de cada expresión sin ejecutar nada. Después compruébalo en el REPL.

a)  17 // 5
b)  17 % 5
c)  17 / 5
d)  2 ** 5
e)  10 - 4 * 2
f)  (10 - 4) * 2
g)  7 // 2 == 3
h)  "alta" == "Alta"
i)  not (3 > 5)
j)  True and False or True
k)  "prio" in "prioridad"
l)  "-" * 5
m)  1 <= 4 <= 3

Ejercicio 2: Reparto de horas en Estudio Alba

Marta reparte el trabajo de un encargo grande. Escribe un programa que, partiendo de estas variables, calcule y muestre lo que se pide:

horas_encargo = 100
personas = 3
HORAS_POR_JORNADA = 8
horas_ya_hechas = 37

Calcula:

  1. Cuántas horas completas toca a cada persona (sin decimales).
  2. Cuántas horas sobran del reparto y no se pueden repartir por igual.
  3. El reparto exacto con decimales.
  4. El porcentaje del encargo ya realizado.
  5. Cuántas jornadas completas de 8 horas quedan por hacer.
  6. Un booleano mas_de_la_mitad que indique si se ha superado el 50 % del encargo.

Ejercicio 3: Reglas de negocio de TareaFácil

Marta ha concretado tres reglas nuevas. Escríbelas como expresiones booleanas guardadas en variables con nombre, partiendo de estas variables de partida:

prioridad = "media"
completada = False
responsable = "Nuria"
dias_restantes = 2
horas_dedicadas = 18
horas_estimadas = 20

Las reglas:

  • necesita_atencion_hoy: la tarea no está completada y, o bien es de prioridad alta, o bien quedan menos de 3 días para la entrega.
  • se_pasa_de_presupuesto: se ha consumido más del 85 % de las horas estimadas y la tarea sigue sin completar.
  • es_de_diseno: el responsable es Luis o Nuria (Marta coordina, no diseña).

Después, indica qué valor tiene cada una con los datos de arriba y añade una prueba de escritorio de necesita_atencion_hoy para tres escenarios distintos.

Soluciones

Solución 1.

Expresión Resultado Tipo Explicación
a 17 // 5 3 int Cociente entero
b 17 % 5 2 int Resto
c 17 / 5 3.4 float / siempre devuelve decimal
d 2 ** 5 32 int 2 elevado a 5
e 10 - 4 * 2 2 int * antes que -: 10 - 8
f (10 - 4) * 2 12 int Los paréntesis cambian el orden
g 7 // 2 == 3 True bool // antes que ==: 3 == 3
h "alta" == "Alta" False bool Distingue mayúsculas
i not (3 > 5) True bool 3 > 5 es False; not False es True
j True and False or True True bool and primero: (True and False) or True → False or True
k "prio" in "prioridad" True bool Es una subcadena
l "-" * 5 "-----" str Repetición de cadena
m 1 <= 4 <= 3 False bool 1 <= 4 sí, pero 4 <= 3 no

Solución 2.

horas_encargo = 100
personas = 3
HORAS_POR_JORNADA = 8
horas_ya_hechas = 37

# 1. Horas completas por persona
horas_por_persona = horas_encargo // personas
print(horas_por_persona)            # 33

# 2. Horas que sobran del reparto
horas_sobrantes = horas_encargo % personas
print(horas_sobrantes)              # 1

# 3. Reparto exacto
reparto_exacto = horas_encargo / personas
print(reparto_exacto)               # 33.333333333333336

# 4. Porcentaje realizado
porcentaje_hecho = horas_ya_hechas / horas_encargo * 100
print(porcentaje_hecho)             # 37.0

# 5. Jornadas completas que quedan
horas_pendientes = horas_encargo - horas_ya_hechas
jornadas_pendientes = horas_pendientes // HORAS_POR_JORNADA
print(horas_pendientes)             # 63
print(jornadas_pendientes)          # 7

# 6. Se ha pasado de la mitad?
mas_de_la_mitad = porcentaje_hecho > 50
print(mas_de_la_mitad)              # False

Comentarios sobre las decisiones tomadas:

  • En 1 y 2 se usa // y % porque el reparto debe ser en horas enteras: 33 horas para cada uno y 1 hora que sobra y habrá que asignar a alguien.
  • En 3 se usa / porque ahí sí interesa el valor exacto. Los decimales interminables se podrán presentar de forma legible con las f-strings de la próxima lección.
  • En 5 se usa //: 63 horas pendientes dan 7 jornadas completas (y sobrarían 7 horas, que se obtendrían con 63 % 8).

Solución 3.

prioridad = "media"
completada = False
responsable = "Nuria"
dias_restantes = 2
horas_dedicadas = 18
horas_estimadas = 20

necesita_atencion_hoy = (not completada) and (prioridad == "alta" or dias_restantes < 3)
print(necesita_atencion_hoy)        # True

porcentaje_consumido = horas_dedicadas / horas_estimadas * 100
se_pasa_de_presupuesto = porcentaje_consumido > 85 and not completada
print(se_pasa_de_presupuesto)       # True

es_de_diseno = responsable in ("Luis", "Nuria")
print(es_de_diseno)                 # True

Las tres son True con los datos de partida:

  • necesita_atencion_hoy: no está completada (True) y, aunque la prioridad no es alta (False), quedan 2 días, que es menos de 3 (True). El or da True y el and también. Los paréntesis alrededor del or son imprescindibles: sin ellos, and se evaluaría primero y la regla significaría otra cosa.
  • se_pasa_de_presupuesto: 18 de 20 horas es el 90 %, que supera el 85 %, y no está completada.
  • es_de_diseno: Nuria está en la tupla de diseñadores.

Prueba de escritorio de necesita_atencion_hoy:

Escenario completada prioridad dias_restantes not completada prioridad == "alta" dias_restantes < 3 or Resultado
Tarea de Nuria, plazo corto False "media" 2 True False True True True
Tarea de Luis, alta, con margen False "alta" 10 True True False True True
Tarea ya entregada, alta True "alta" 1 False True True True False

El tercer escenario es el que valida el diseño de la regla: por muy alta que sea la prioridad y por muy cerca que esté el plazo, una tarea completada no necesita atención. El not completada en la parte izquierda del and corta cualquier otra consideración, y además lo hace por cortocircuito: al ser False, Python ni siquiera evalúa el paréntesis del or.

Conclusión

Tus variables han dejado de estar quietas. Ahora sabes que una expresión es cualquier combinación de valores, variables y operadores que Python reduce a un único valor, y que se evalúa sustituyendo primero cada nombre por su valor y aplicando después los operadores en orden de precedencia.

Manejas los siete operadores aritméticos, con la distinción crítica entre / (siempre float) y // (entero), y sabes que % no es una curiosidad matemática sino la herramienta estándar para descomponer cantidades y detectar múltiplos. Escribes incrementos con asignación compuesta (+=). Sabes que las comparaciones producen booleanos que puedes guardar y nombrar, que los operadores lógicos and, or y not se comportan según tablas de verdad predecibles, y que su evaluación perezosa no solo ahorra trabajo sino que permite proteger operaciones peligrosas colocando la comprobación a la izquierda. Usas in para preguntar por pertenencia, reservas is exclusivamente para None, concatenas y repites cadenas con + y *, y pones paréntesis siempre que la precedencia no sea obvia.

TareaFácil está en su versión 0.3 y ya calcula: días restantes hasta la entrega, jornadas invertidas, porcentaje de avance del encargo de la Panadería Solé y una expresión de urgencia (prioridad == "alta" and not completada) que traduce a código una regla de negocio real de Marta.

Y sin embargo el programa sigue teniendo dos carencias evidentes. La primera es de presentación: cada dato necesita su propio print y la ficha resultante es ilegible; no hemos podido mezclar texto y valores en una sola línea porque "Dias: " + 3 da TypeError. La segunda es más de fondo: todos los datos siguen escritos dentro del código. Para registrar otra tarea hay que abrir el editor.

Las dos se resuelven en la siguiente lección, Entrada y salida de datos, donde aprenderás a dar formato profesional a la salida con f-strings y a pedirle los datos al usuario por teclado con input(). A partir de ahí, TareaFácil dejará de ser un programa que se edita para convertirse en un programa que se usa.

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