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
- Qué es una expresión y cómo se evalúa
- Operadores aritméticos
- División entera
//y módulo%: los dos infravalorados - Asignación compuesta
- Operadores de comparación
- Operadores lógicos y tabla de verdad
- Evaluación perezosa (cortocircuito)
- El operador de pertenencia
in ==frente ais- Operadores con cadenas:
+y* - Precedencia de operadores
- TareaFácil: calcular sobre los datos de la tarea
- Errores comunes y consejos
- Ejercicios
- Conclusión
- 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:
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) # 24Python hace lo siguiente al llegar a la tercera línea:
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.
- 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) # 343Hay 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:
Los signos negativos funcionan como esperas, y el - también puede usarse con un solo operando (se llama menos unario):
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.
- División entera
// y módulo %: los dos infravalorados
// y módulo %: los dos infravaloradosEstos 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) # 15195 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) # 3El 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 5n % 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.
- 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:
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.5Fí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):
- 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) # TrueEse 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 alfabeticoLa 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:
Eso se lee tal cual: «días está entre 1 y 7, ambos incluidos».
- 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) # TrueDesmenucemos esa expresión, porque condensa casi todo lo visto hasta ahora:
prioridad == "alta"es una comparación →True.completadavaleFalse, así quenot completada→True.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) # Falsenot 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) # TrueNo 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 booleanoEn 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:
O, mejor todavía, usa el operador in que veremos en el apartado 8.
- 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: siAya esFalse, el resultado seráFalsepase lo que pase conB. Python ni siquiera evalúaB. - En
A or B: siAya esTrue, el resultado seráTrue. Python no evalúaB.
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 ZeroDivisionErrorSi 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.
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.
- El operador de pertenencia
in
inEl 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 mayusculasSu forma negada es not in:
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) # TrueLos 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.
== frente a is
== frente a isEstos 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?ispregunta: ¿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 internasEsa 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:
¿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.
- Operadores con cadenas:
+ y *
+ 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 SoleFí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:
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 |
- 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.
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:
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.
- 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) # FalseVamos 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 <= 3Ejercicio 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:
Calcula:
- Cuántas horas completas toca a cada persona (sin decimales).
- Cuántas horas sobran del reparto y no se pueden repartir por igual.
- El reparto exacto con decimales.
- El porcentaje del encargo ya realizado.
- Cuántas jornadas completas de 8 horas quedan por hacer.
- Un booleano
mas_de_la_mitadque 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 = 20Las 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) # FalseComentarios 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 con63 % 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) # TrueLas 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). ElordaTruey elandtambién. Los paréntesis alrededor delorson imprescindibles: sin ellos,andse 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
- ¿Qué es la programación?
- Historia de la programación
- Lenguajes de programación
- Entornos de desarrollo
- Del problema al algoritmo
Módulo 2: Conceptos Básicos
- Variables y tipos de datos
- Operadores y expresiones
- Entrada y salida de datos
- Conversión de tipos y validación de datos
Módulo 3: Estructuras de Control
Módulo 4: Funciones y Procedimientos
- Definición y uso de funciones
- Parámetros y retorno de valores
- Ámbito de variables
- Descomponer un programa en funciones
- Funciones como valores: lambda y orden superior
Módulo 5: Estructuras de Datos
- Listas y arreglos
- Cadenas de caracteres
- Diccionarios y conjuntos
- Tuplas y estructuras anidadas
- Guardar datos en archivos: texto, CSV y JSON
Módulo 6: Algoritmos Básicos
Módulo 7: Objetos y Organización del Código
- De los datos a los objetos: clases e instancias
- Atributos, métodos y constructor
- Colecciones de objetos
- Módulos, paquetes e importaciones
Módulo 8: Buenas Prácticas y Herramientas
- Documentación y comentarios
- Depuración y manejo de errores
- Control de versiones
- Pruebas automatizadas
- Estilo, legibilidad y refactorización
