Operadores en Python — ejercicios con la cuota del gimnasio, segundos a horas y el cambio de la caja moneda a moneda (y un or que siempre decía que sí)

En una práctica de FP1 teníamos que simular la máquina de cobro de la cafetería, y una de las comprobaciones era si el método de pago estaba aceptado. Yo escribí metodo == "efectivo" or "bizum", lo probé con efectivo, lo probé con bizum, funcionaba… y lo entregué. El profesor lo probó con «tarjeta» y la máquina también la aceptó. Y con «piedras». Mi or decía que sí a todo. Tardé un buen rato en entender por qué, y es uno de los tres fragmentos de «¿Qué falla aquí?» de hoy: los operadores no hacen lo que lees, hacen lo que dicen sus reglas.

Estos son los ejercicios del tema de operadores. Antes conviene haber leído la teoría y hecho la práctica. Tienes cinco ejercicios en tres niveles (básico, intermedio y desafío) con especial foco en // y %, que son los que más confunden, tres fragmentos con trampa, las soluciones explicadas línea a línea y, al final, el chuletario para tenerlo todo a mano.

Cómo usar este artículo

  1. Lee el enunciado y la salida esperada. Los datos van fijos en variables al principio (sin input()), igual que en la práctica.
  2. Intenta resolverlo en tu editor. Si llevas más de 10 minutos atascado, abre la pista.
  3. Compara con la solución comentada. No solo si da lo mismo: fíjate en por qué está hecha así.
  4. Cambia los datos (otro número de meses, otra nota, otro importe) y predice la salida antes de ejecutar.

Todos se pueden hacer sin if, solo con operadores: es parte del reto. En el siguiente tema, con los condicionales, verás versiones más cómodas.

Nivel básico

Ejercicio 1: factura del gimnasio

Un gimnasio cobra 29,99 € al mes. Los socios que llevan más de 12 meses tienen un 10 % de descuento a partir del mes 13. Con meses = 15, el programa muestra la cuota base, la cuota de este mes, el total pagado hasta ahora y el ahorro acumulado. Trabaja en céntimos.

Salida esperada:

--- Factura del gimnasio ---
Meses como socio: 15
Cuota base: 29,99 €
Cuota este mes: 26,99 €
Total pagado: 440,85 €
Ahorro acumulado: 9,00 €

Separa los meses en dos tramos: los que pagan precio completo y los que tienen descuento (max(0, meses - 12) no deja que salga negativo). Y recuerda que True vale 1 y False vale 0 cuando se multiplican.

Ejercicio 2: conversor de tiempo

Convierte segundos = 3723 a horas, minutos y segundos, y muéstralo también en formato reloj (01:02:03). Comprueba el resultado haciendo la cuenta al revés y repite la conversión con divmod.

Salida esperada:

3723 segundos son:
→ 1 h, 2 min y 3 s
→ en formato reloj: 01:02:03
Comprobación: 3723
Con divmod: 1 2 3

Es puro // y %. Las horas son segundos // 3600; lo que sobra de las horas es segundos % 3600, y de eso salen los minutos con // 60. Los segundos sueltos son segundos % 60.

Nivel intermedio

Ejercicio 3: análisis de una nota

Con una nota sobre 100 (nota = 73), muestra si es válida (entre 0 y 100), la nota sobre 10, si está aprobada (≥ 50), si es notable (≥ 70) o sobresaliente (≥ 90), cuál es el siguiente nivel y cuántos puntos faltan para llegar, y cuántos puntos tiene por encima del aprobado. Sin if.

Salida esperada:

--- Análisis de la nota 73 ---
¿Nota válida? True
Sobre 10: 7.3
¿Aprobado? True | ¿Notable? True | ¿Sobresaliente? False
Siguiente nivel: Sobresaliente (faltan 17 puntos)
Puntos sobre el aprobado: 23

(nota < 70 and "Notable") vale "Notable" si la nota es menor que 70 y False si no. Encadena varios así con or: el primero que no sea falso es el que se queda.

Ejercicio 4: extractor de dígitos

Con n = 2847, saca cada cifra por separado sin convertir el número a texto, solo con // y %. Muestra también la suma de las cifras, si el número es divisible entre 3 (comprobándolo de dos formas), si es par, el número al revés y si de verdad tiene 4 cifras.

Salida esperada:

--- Dígitos de 2847 ---
Millares: 2 | Centenas: 8 | Decenas: 4 | Unidades: 7
Suma de dígitos: 21
¿Divisible entre 3? True | comprobación: True
¿Es par? False
Al revés: 7482
¿Tiene 4 cifras? True

n % 10 es la última cifra y n // 10 quita la última cifra. Combinándolos: las decenas son n // 10 % 10. Un número es divisible entre 3 si la suma de sus cifras lo es.

Desafío final

Ejercicio 5: gestor de caja registradora

Una compra de 37,43 € pagada con 50 €. Calcula el cambio y desglósalo en el mínimo número de piezas: billetes de 50, 20, 10 y 5 € y monedas de 2 €, 1 €, 50, 20, 10, 5, 2 y 1 céntimo. Muestra cuántas piezas de cada tipo, el total de piezas y una comprobación de que el desglose suma exactamente el cambio. Todavía no sabemos hacer bucles, así que cada pieza va en su línea.

Salida esperada:

Cambio con floats: 12.57
--- Caja: 12,57 € de cambio ---
Billetes: 50 €: 0 | 20 €: 0 | 10 €: 1 | 5 €: 0
Monedas: 2 €: 1 | 1 €: 0 | 50 cént.: 1 | 20 cént.: 0
Monedas: 10 cént.: 0 | 5 cént.: 1 | 2 cént.: 1 | 1 cént.: 0
Piezas en total: 5
¿Cuadra el desglose? True

Pasa el cambio a céntimos con round(cambio * 100) y no vuelvas a tocar un float. Para cada pieza, divmod(resto, valor) te da cuántas caben y lo que queda, y ese resto es el que pasa a la siguiente pieza.

¿Qué falla aquí?

Tres fragmentos cortos con un error cada uno. Antes de leer la explicación, predice la salida.

Fragmento 1: el precio medio

cafe1 = 120
cafe2 = 110
cafe3 = 140
media = cafe1 + cafe2 + cafe3 / 3
print("Precio medio:", media, "céntimos")

¿Qué crees que imprime? El precio medio de tres cafés de 120, 110 y 140 céntimos debería ser 123,33.

Precio medio: 276.6666666666667 céntimos

Qué pasa: la precedencia. / va antes que +, así que Python calcula cafe3 / 3 = 46,67 y después le suma los otros dos: 120 + 110 + 46,67 = 276,67. Solo se ha dividido el último café.

Escribir la media «como se dice» (suma los tres y divide entre tres) sin paréntesis. La división solo afecta al operando que tiene pegado.

El arreglo: paréntesis alrededor de la suma.

cafe1 = 120
cafe2 = 110
cafe3 = 140
media = (cafe1 + cafe2 + cafe3) / 3      # paréntesis: primero sumar, luego dividir
print("Precio medio:", round(media, 2), "céntimos")

Esto es lo que imprime:

Precio medio: 123.33 céntimos

Ahora (120 + 110 + 140) / 3 = 370 / 3 = 123,33.

Cuando escribas una media, escribe primero los paréntesis vacíos ( ) / n y rellena la suma dentro. Así no se te olvidan nunca.

Fragmento 2: el ticket de tres cafés

total = 3 * 1.10
print("Total:", total)
print("¿Son 3,30 €?", total == 3.30)

Tres cafés de 1,10 €. ¿Son 3,30 €?

Total: 3.3000000000000003
¿Son 3,30 €? False

Qué pasa: 1,10 no se puede guardar exacto en binario, y al multiplicarlo por 3 el error se hace visible: 3.3000000000000003. Comparado con el 3.30 escrito a mano (que tampoco es exacto, pero es otro número), == da False. Es el mismo problema que el 0.1 + 0.2 de la teoría.

Comparar el resultado de una cuenta con decimales usando ==. Puede funcionar con unos datos y fallar con otros.

El arreglo: comparar con margen o, mejor todavía, trabajar en céntimos.

import math

total = 3 * 1.10
print("¿Son 3,30 €?", math.isclose(total, 3.30))   # compara con margen

total_cent = 3 * 110                                 # o mejor: en céntimos
print("¿Son 330 céntimos?", total_cent == 330)

Salida real:

¿Son 3,30 €? True
¿Son 330 céntimos? True

Si tu programa maneja dinero, decide desde la primera línea que todo va en céntimos (int) y conviértelo a euros solo al mostrarlo.

Fragmento 3: el or que siempre decía que sí

El de mi anécdota:

metodo = "tarjeta"
acepta = metodo == "efectivo" or "bizum"
print("¿Se acepta?", acepta)

El método es «tarjeta», que no está entre los aceptados. ¿Qué imprime?

¿Se acepta? bizum

Qué pasa: no imprime False, ni siquiera True: imprime bizum. Python no reparte el == entre los dos lados del or. Lo que hace es:

  1. Evalúa la izquierda: metodo == "efectivo" → False.
  2. Como la izquierda es falsa, or devuelve la derecha tal cual: el texto "bizum".
  3. Un texto no vacío es verdadero, así que en un if contaría como «aceptado». Con cualquier método.
Por qué metodo == "efectivo" or "bizum" siempre aceptaArriba, lo que cree el lector: comparar metodo con "efectivo" o con "bizum". Abajo, lo que hace Python: evalúa metodo == "efectivo", que con "tarjeta" es False, y como es falso or devuelve el operando de la derecha, "bizum", sin compararlo con nada. Un texto no vacío es verdadero, así que todo se acepta.Lo que leesmetodo == "efectivo" or "bizum"¿metodo es "efectivo" o "bizum"?Lo que hace Python(metodo == "efectivo")metodo = "tarjeta" → Falseor"bizum"texto no vacío = verdaderoFalse → sigue a la derechadevuelve "bizum" tal cualacepta vale "bizum"Arreglo: metodo == "efectivo" or metodo == "bizum" → False
or no reparte el == entre los dos lados: devuelve el primer valor verdadero que encuentra, aquí el texto "bizum".

x == a or b para preguntar «¿x es a o b?». Siempre es «verdadero» en cuanto b no es vacío ni cero. Hay que escribir la comparación completa en los dos lados.

El arreglo:

metodo = "tarjeta"
acepta = metodo == "efectivo" or metodo == "bizum"   # comparar en los dos lados
print("¿Se acepta?", acepta)

Y el resultado:

¿Se acepta? False

Y con metodo = "bizum", para comprobar que lo aceptado sigue funcionando:

¿Se acepta? True

Cuando haya muchas opciones, metodo in ("efectivo", "bizum", "tarjeta") es más corto y no tiene trampa. in pregunta si un valor está dentro de una colección; lo verás a fondo con las listas y tuplas.

Soluciones comentadas

Solución ejercicio 1

# Nivel 1 · Factura del gimnasio (todo en céntimos)
CUOTA_CENT = 2999            # 29,99 € al mes
DESCUENTO_PCT = 10           # a partir del mes 13
MESES_SIN_DESCUENTO = 12
meses = 15                   # meses que lleva el socio (antes: input)

# Tramos: los 12 primeros a precio completo y el resto con descuento
meses_con_descuento = max(0, meses - MESES_SIN_DESCUENTO)   # max evita negativos
meses_sin = meses - meses_con_descuento

descuento_cent = round(CUOTA_CENT * DESCUENTO_PCT / 100)    # 299.9 → 300
cuota_rebajada = CUOTA_CENT - descuento_cent

# True vale 1 y False vale 0: si no hay descuento, se resta 0
tiene_descuento = meses > MESES_SIN_DESCUENTO
cuota_mes = CUOTA_CENT - descuento_cent * tiene_descuento

total = meses_sin * CUOTA_CENT + meses_con_descuento * cuota_rebajada
ahorro = meses_con_descuento * descuento_cent

print("--- Factura del gimnasio ---")
print("Meses como socio:", meses)
print(f"Cuota base: {CUOTA_CENT // 100},{CUOTA_CENT % 100:02d} €")
print(f"Cuota este mes: {cuota_mes // 100},{cuota_mes % 100:02d} €")
print(f"Total pagado: {total // 100},{total % 100:02d} €")
print(f"Ahorro acumulado: {ahorro // 100},{ahorro % 100:02d} €")

Al ejecutarlo:

--- Factura del gimnasio ---
Meses como socio: 15
Cuota base: 29,99 €
Cuota este mes: 26,99 €
Total pagado: 440,85 €
Ahorro acumulado: 9,00 €

Por qué está hecho así:

  • Céntimos desde el principio: 29,99 € son 2999 céntimos. El descuento del 10 % es 299,9 céntimos, que round deja en 300 (redondeo al céntimo más cercano; con // saldría 299 y el gimnasio te cobraría un céntimo de más cada mes).
  • Los tramos sin if: max(0, meses - 12) da 3 con 15 meses y 0 con 8 (porque 8 − 12 = −4 y max se queda con el 0). Así meses_sin es 15 − 3 = 12.
  • El truco del bool: tiene_descuento es True con 15 meses. Multiplicado, True vale 1: 2999 - 300 * True = 2699. Con 8 meses sería 2999 - 300 * False = 2999.

Las cuentas: total = 12 × 2999 + 3 × 2699 = 35988 + 8097 = 44085 céntimos (440,85 €); ahorro = 3 × 300 = 900 céntimos (9,00 €). La versión antigua de este ejercicio daba 434,85 €: estaba mal. Por eso aquí todas las salidas son de una ejecución real.

Con meses = 8, sin descuento:

--- Factura del gimnasio ---
Meses como socio: 8
Cuota base: 29,99 €
Cuota este mes: 29,99 €
Total pagado: 239,92 €
Ahorro acumulado: 0,00 €

8 × 2999 = 23992 céntimos, 239,92 €, y ahorro 0,00 €: el :02d es el que hace que salgan los dos ceros.

Calcular meses - 12 sin max. Con menos de 12 meses salen «meses con descuento» negativos y el total baja de golpe.

Solución ejercicio 2

# Nivel 1 · Conversor de tiempo: segundos → horas, minutos y segundos
segundos = 3723

horas = segundos // 3600              # horas completas
minutos = segundos % 3600 // 60       # (lo que sobra de las horas) // 60
segs = segundos % 60                  # lo que no llega a un minuto

print(segundos, "segundos son:")
print("→", horas, "h,", minutos, "min y", segs, "s")
print(f"→ en formato reloj: {horas:02d}:{minutos:02d}:{segs:02d}")

# Comprobación a la inversa y la versión con divmod
print("Comprobación:", horas * 3600 + minutos * 60 + segs)
h, resto = divmod(segundos, 3600)
m, s = divmod(resto, 60)
print("Con divmod:", h, m, s)

Esto es lo que imprime:

3723 segundos son:
→ 1 h, 2 min y 3 s
→ en formato reloj: 01:02:03
Comprobación: 3723
Con divmod: 1 2 3

Por qué está hecho así:

  • segundos % 3600 // 60 no lleva paréntesis porque % y // están en el mismo escalón y se hacen de izquierda a derecha: primero 3723 % 3600 = 123 (lo que sobra de la hora) y luego 123 // 60 = 2 minutos.
  • segundos % 60 = 3 da directamente los segundos sueltos: da igual cuántas horas haya, el resto entre 60 es lo que no llega a un minuto.
  • :02d rellena con ceros para el formato reloj.
  • La comprobación a la inversa: 1 × 3600 + 2 × 60 + 3 = 3723.
  • La versión con divmod hace lo mismo en dos pasos y se lee casi como una frase: «divide entre 3600 y quédate con el resto; divide el resto entre 60».
PasoOperaciónResultado
Horas3723 // 36001
Resto tras las horas3723 % 3600123
Minutos123 // 602
Segundos3723 % 603

Con un dato más grande, segundos = 45296:

45296 segundos son:
→ 12 h, 34 min y 56 s
→ en formato reloj: 12:34:56
Comprobación: 45296
Con divmod: 12 34 56

45296 // 3600 = 12 (12 × 3600 = 43200); sobran 2096; 2096 // 60 = 34 (34 × 60 = 2040); y 45296 % 60 = 56.

Calcular los minutos como segundos // 60 a secas: con 3723 da 62, que son los minutos totales, no los que sobran después de quitar la hora.

Solución ejercicio 3

# Nivel 2 · Análisis de una nota sobre 100, sin if: solo operadores
nota = 73

sobre_10 = nota / 10
en_rango = 0 <= nota <= 100          # comparación encadenada
aprobado = nota >= 50
notable = nota >= 70
sobresaliente = nota >= 90

# and/or devuelven un operando: el primer tramo que se cumpla «gana»
siguiente = (nota < 50 and "Aprobado") or (nota < 70 and "Notable") \
    or (nota < 90 and "Sobresaliente") or "ninguno, ya es la máxima"
faltan = (nota < 50 and 50 - nota) or (nota < 70 and 70 - nota) \
    or (nota < 90 and 90 - nota) or 0
sobre_aprobado = max(0, nota - 50)

print("--- Análisis de la nota", nota, "---")
print("¿Nota válida?", en_rango)
print("Sobre 10:", sobre_10)
print("¿Aprobado?", aprobado, "| ¿Notable?", notable, "| ¿Sobresaliente?", sobresaliente)
print("Siguiente nivel:", siguiente, "(faltan", faltan, "puntos)")
print("Puntos sobre el aprobado:", sobre_aprobado)

Salida real:

--- Análisis de la nota 73 ---
¿Nota válida? True
Sobre 10: 7.3
¿Aprobado? True | ¿Notable? True | ¿Sobresaliente? False
Siguiente nivel: Sobresaliente (faltan 17 puntos)
Puntos sobre el aprobado: 23

Por qué está hecho así: lo interesante son las dos expresiones con and y or.

siguiente = (nota < 50 and "Aprobado") or (nota < 70 and "Notable") \
    or (nota < 90 and "Sobresaliente") or "ninguno, ya es la máxima"
faltan = (nota < 50 and 50 - nota) or (nota < 70 and 70 - nota) \
    or (nota < 90 and 90 - nota) or 0

Así se evalúa siguiente con nota = 73, de izquierda a derecha, parando en cuanto algo es verdadero:

TrozoCuentaValor¿Sigue?
nota < 50 and "Aprobado"73 < 50 es False; and se paraFalseSí, or mira el siguiente
nota < 70 and "Notable"73 < 70 es FalseFalseSí
nota < 90 and "Sobresaliente"73 < 90 es True; and devuelve el texto"Sobresaliente"No: es verdadero, or se lo queda

faltan funciona igual y llega a 90 - nota = 17. Puntos sobre el aprobado: max(0, 73 - 50) = 23. Y 0 <= nota <= 100 es la comparación encadenada que comprueba el rango.

Con nota = 42 y con nota = 95:

--- Análisis de la nota 42 ---
¿Nota válida? True
Sobre 10: 4.2
¿Aprobado? False | ¿Notable? False | ¿Sobresaliente? False
Siguiente nivel: Aprobado (faltan 8 puntos)
Puntos sobre el aprobado: 0

Y la segunda:

--- Análisis de la nota 95 ---
¿Nota válida? True
Sobre 10: 9.5
¿Aprobado? True | ¿Notable? True | ¿Sobresaliente? True
Siguiente nivel: ninguno, ya es la máxima (faltan 0 puntos)
Puntos sobre el aprobado: 45

Con 42, el primer trozo ya es verdadero: siguiente «Aprobado», faltan 50 − 42 = 8, y max(0, 42 - 50) = 0 puntos sobre el aprobado. Con 95, ningún trozo se cumple y or llega al último valor: el texto «ninguno, ya es la máxima» y 0 puntos.

Esta técnica falla si el valor que quieres devolver es «falso». Por ejemplo, si una nota estuviera justo en el límite y el cálculo diera 70 - 70 = 0, or lo saltaría porque 0 es falso. Aquí no pasa porque cada trozo solo se cumple cuando la diferencia es positiva, pero cuando tengas if (siguiente tema) usa if para esto.

Solución ejercicio 4

# Nivel 2 · Extractor de dígitos de un número de 4 cifras, sin pasarlo a texto
n = 2847

unidades = n % 10              # la última cifra
decenas = n // 10 % 10         # quitar la última y quedarse con la nueva última
centenas = n // 100 % 10
millares = n // 1000           # con 4 cifras, lo que queda delante

suma = millares + centenas + decenas + unidades
al_reves = unidades * 1000 + decenas * 100 + centenas * 10 + millares

print("--- Dígitos de", n, "---")
print("Millares:", millares, "| Centenas:", centenas, "| Decenas:", decenas, "| Unidades:", unidades)
print("Suma de dígitos:", suma)
print("¿Divisible entre 3?", suma % 3 == 0, "| comprobación:", n % 3 == 0)
print("¿Es par?", unidades % 2 == 0)
print("Al revés:", al_reves)
print("¿Tiene 4 cifras?", 1000 <= n <= 9999)

Y el resultado:

--- Dígitos de 2847 ---
Millares: 2 | Centenas: 8 | Decenas: 4 | Unidades: 7
Suma de dígitos: 21
¿Divisible entre 3? True | comprobación: True
¿Es par? False
Al revés: 7482
¿Tiene 4 cifras? True

Por qué está hecho así: cada cifra sale de «quitar las de la derecha» con // y «quedarse con la última» con % 10.

CifraExpresiónCuentaResultado
Unidadesn % 102847 % 107
Decenasn // 10 % 10284 % 104
Centenasn // 100 % 1028 % 108
Millaresn // 10002847 // 10002

n // 10 % 10 no necesita paréntesis por la misma razón que en el ejercicio 2: mismo escalón, de izquierda a derecha. La suma es 2 + 8 + 4 + 7 = 21, y 21 % 3 = 0, así que 2847 es divisible entre 3 (2847 = 3 × 949, y n % 3 == 0 lo confirma). El número al revés se construye multiplicando cada cifra por su nueva posición: 7 × 1000 + 4 × 100 + 8 × 10 + 2 = 7482. Y 1000 <= n <= 9999 comprueba que de verdad tiene 4 cifras.

Sacar los millares con n // 1000 % 10 está bien, pero si el número tuviera 5 cifras (12847) n // 1000 daría 12. Por eso la última línea comprueba el rango.

Solución ejercicio 5

# Nivel 3 · Caja registradora: el cambio, billete a billete y moneda a moneda
importe = 37.43
entregado = 50.00

cambio = entregado - importe
print("Cambio con floats:", cambio)          # el float no es exacto
resto = round(cambio * 100)                   # pasamos a céntimos ENTEROS
total_cent = resto                            # lo guardamos para comprobar al final

# divmod(resto, valor) devuelve (cuántas piezas caben, lo que sobra)
b50, resto = divmod(resto, 5000)
b20, resto = divmod(resto, 2000)
b10, resto = divmod(resto, 1000)
b5, resto = divmod(resto, 500)
m200, resto = divmod(resto, 200)
m100, resto = divmod(resto, 100)
m50, resto = divmod(resto, 50)
m20, resto = divmod(resto, 20)
m10, resto = divmod(resto, 10)
m5, resto = divmod(resto, 5)
m2, resto = divmod(resto, 2)
m1 = resto                                    # lo último que queda son céntimos sueltos

print(f"--- Caja: {total_cent // 100},{total_cent % 100:02d} € de cambio ---")
print("Billetes: 50 €:", b50, "| 20 €:", b20, "| 10 €:", b10, "| 5 €:", b5)
print("Monedas: 2 €:", m200, "| 1 €:", m100, "| 50 cént.:", m50, "| 20 cént.:", m20)
print("Monedas: 10 cént.:", m10, "| 5 cént.:", m5, "| 2 cént.:", m2, "| 1 cént.:", m1)

piezas = b50 + b20 + b10 + b5 + m200 + m100 + m50 + m20 + m10 + m5 + m2 + m1
suma = (b50 * 5000 + b20 * 2000 + b10 * 1000 + b5 * 500 + m200 * 200 + m100 * 100
        + m50 * 50 + m20 * 20 + m10 * 10 + m5 * 5 + m2 * 2 + m1)
print("Piezas en total:", piezas)
print("¿Cuadra el desglose?", suma == total_cent)

Al ejecutarlo:

Cambio con floats: 12.57
--- Caja: 12,57 € de cambio ---
Billetes: 50 €: 0 | 20 €: 0 | 10 €: 1 | 5 €: 0
Monedas: 2 €: 1 | 1 €: 0 | 50 cént.: 1 | 20 cént.: 0
Monedas: 10 cént.: 0 | 5 cént.: 1 | 2 cént.: 1 | 1 cént.: 0
Piezas en total: 5
¿Cuadra el desglose? True

Por qué está hecho así:

  • Un solo paso con decimales: entregado - importe es la única cuenta con float, y justo después se pasa a céntimos con round(cambio * 100). Aquí la resta se imprime como 12.57, pero no siempre tendrás esa suerte (prueba con importe = 0.10 y entregado = 0.30): round garantiza un entero correcto pase lo que pase. Con int(cambio * 100) podrías quedarte un céntimo corto si el float fuera 1256,9999…
  • divmod en cascada: cada línea pieza, resto = divmod(resto, valor) guarda cuántas piezas caben y sobrescribe resto con lo que queda, que es lo que recibe la línea siguiente. Es una asignación doble: divmod devuelve una pareja y Python reparte sus dos valores en las dos variables.
  • De mayor a menor: así se consigue el mínimo de piezas (con euros funciona siempre).

Así se va comiendo el cambio, pieza a pieza:

Desglose del cambio con divmodSe parte de 1257 céntimos. Para cada billete o moneda, de 50 euros a 1 céntimo, divmod da cuántas piezas caben y el resto. Caben 1 de 10 euros (resto 257), 1 de 2 euros (resto 57), 1 de 50 céntimos (resto 7), 1 de 5 céntimos (resto 2) y 1 de 2 céntimos (resto 0). Las demás dan 0.1257 céntimos de cambio, pieza a pieza50 €× 0resto 125720 €× 0resto 125710 €× 1resto 2575 €× 0resto 2572 €× 1resto 571 €× 0resto 5750 cént.× 1resto 720 cént.× 0resto 710 cént.× 0resto 75 cént.× 1resto 22 cént.× 1resto 01 cént.× 0resto 0piezas: 5 · comprobación: 1000 + 200 + 50 + 5 + 2 = 1257
Cada divmod se come lo que puede y pasa el resto a la siguiente pieza; las que caben resaltadas en coral.
PiezadivmodCabenResto
10 €divmod(1257, 1000)1257
5 €divmod(257, 500)0257
2 €divmod(257, 200)157
1 €divmod(57, 100)057
50 cént.divmod(57, 50)17
20 y 10 cént.divmod(7, 20), divmod(7, 10)07
5 cént.divmod(7, 5)12
2 cént.divmod(2, 2)10

(50 € y 20 € dan 0 con resto 1257 porque no caben.) Total: 5 piezas, y la comprobación 1000 + 200 + 50 + 5 + 2 = 1257 da True.

Hacer el desglose con float (cambio // 0.05). Con decimales, // puede dar una pieza de menos por un error minúsculo y dejar un resto de 0,0499999. En céntimos enteros nunca pasa.


Cuestionario tipo examen

Ahora ponte a prueba como en el examen. Los cuestionarios de FP1 son así: 15 preguntas en 20 minutos (16 en 30 en el de Secuencias), casi todas de respuesta única (cada fallo resta un tercio de lo que vale la pregunta) y muchas de «¿qué imprime este código?», con algún hueco, relacionar o verdadero o falso. Aquí tienes 13 preguntas originales sobre operadores. Contesta sin ejecutar nada, pulsa Comprobar y lee la explicación de cada una. Estas preguntas son también el banco del simulacro de teoría de FP1, que las mezclará con las de los demás temas.

Pregunta 1Respuesta corta

¿Qué imprime este programa? Escribe la salida exacta.

print(17 // 5, 17 % 5)
Solución

Respuesta: 3 2

// da el cociente entero y % el resto: 17 = 5 × 3 + 2. Es la pareja que usarás para pasar segundos a minutos, sacar cifras de un número o saber si es par.

Pregunta 2Única

¿Qué valor muestra este código?

a = True
b = False
print(not a or b and a)
  1. False
  2. True
  3. Un error de sintaxis
  4. None
Solución

Respuesta: False

Prioridad de mayor a menor: not, and, or. Se calcula como (not a) or (b and a) = False or False = False. Si dudas, pon paréntesis: nadie te va a quitar puntos por usarlos.

Pregunta 3Respuesta corta

¿Qué imprime este programa? Escribe la salida exacta.

print(2 + 3 * 4 ** 2)
Solución

Respuesta: 50

Primero la potencia: 4 ** 2 = 16. Después la multiplicación: 3 × 16 = 48. Y al final la suma: 2 + 48 = 50.

Pregunta 4Única

¿Cuál de estas expresiones es True cuando n es par?

  1. n % 2 == 0
  2. n / 2 == 0
  3. n // 2 == 1
  4. n % 2 == 1
Solución

Respuesta: n % 2 == 0

% es el operador estrella para preguntar «¿es múltiplo de…?». n % 2 == 0 → par; n % 3 == 0 → múltiplo de 3.

Pregunta 5Relacionar

Relaciona cada expresión con su resultado:

7 / 2
7 // 2
7 % 2
2 ** 3

Definiciones:

  • 1
  • 6
  • 14
  • 3.5
  • 3
  • 8
Solución
  • 7 / 2 → 3.5
  • 7 // 2 → 3
  • 7 % 2 → 1
  • 2 ** 3 → 8

/ es división con decimales, // cociente entero, % resto y ** potencia. 2 ** 3 = 2 × 2 × 2 = 8 (no 6: no es una multiplicación).

Pregunta 6Única

Un compañero quiere comprobar si color es 'rojo' o 'azul' y escribe color == 'rojo' or 'azul'. ¿Qué pasa?

  1. La condición es siempre verdadera, porque 'azul' sola cuenta como True
  2. Funciona bien
  3. Da un error de sintaxis
  4. Es siempre falsa
Solución

Respuesta: La condición es siempre verdadera, porque 'azul' sola cuenta como True

Python lee (color == 'rojo') or ('azul'). Si la primera parte es falsa, el or devuelve 'azul', y un texto no vacío es «verdadero» en un if. Es uno de los fallos más típicos de FP1.

Pregunta 7Respuesta corta

¿Qué imprime este programa? Escribe la salida exacta.

total = 10
total += 5
total *= 2
total -= 4
print(total)
Solución

Respuesta: 26

+=, *= y -= son abreviaturas: 10 + 5 = 15, 15 × 2 = 30, 30 − 4 = 26.

Pregunta 8Verdadero o falso

Indica si es verdadero o falso:

  1. 5 == 5.0 es True.
  2. '5' == 5 es True.
  3. 3 < 5 < 9 es True.
  4. = y == hacen lo mismo.
Solución
  • V — 5 == 5.0 es True.
  • F — '5' == 5 es True.
  • V — 3 < 5 < 9 es True.
  • F — = y == hacen lo mismo.

Un int y un float con el mismo valor son iguales, pero un texto nunca es igual a un número. Python permite encadenar comparaciones (3 < 5 < 9 es 3 < 5 and 5 < 9). Y = asigna, mientras que == compara.

Pregunta 9Única

¿Qué valor da round(2.5) en Python?

  1. 2
  2. 3
  3. 2.5
  4. Un error
Solución

Respuesta: 2

Con los empates (x,5), round va al par: round(2.5) es 2 y round(3.5) es 4. Es una trampa que cae en más de un cuestionario.

Pregunta 10Única

¿Qué imprime este código?

x = 0
print(x != 0 and 10 / x > 1)
  1. False
  2. Un error ZeroDivisionError
  3. True
  4. 0
Solución

Respuesta: False

and y or evalúan en cortocircuito: si la primera parte del and es falsa, ya saben el resultado y no miran la segunda. Así se protege una división: «comprueba antes de dividir».

Pregunta 11Rellenar huecos

Completa para pasar 135 segundos a minutos y segundos (2 min 15 s):

minutos = 135 ____ 60 y segundos = 135 ____ 60

Palabras:

  • %
  • //
  • **
  • /
Solución

minutos = 135 // 60 y segundos = 135 % 60

135 // 60 = 2 minutos y 135 % 60 = 15 segundos. Con / saldría 2.25, que no son minutos y segundos.

Pregunta 12Única

¿Qué imprime este programa?

print("ja" * 3 + "!")
  1. jajaja!
  2. ja3!
  3. Un error
  4. ja ja ja !
Solución

Respuesta: jajaja!

Con textos, + concatena y * n repite n veces. La multiplicación va antes: 'ja' * 3 = 'jajaja', y después se le une el '!'.

Pregunta 13Única

¿Qué valor tiene -7 // 2?

  1. -4
  2. -3
  3. -3.5
  4. 3
Solución

Respuesta: -4

-7 / 2 = -3.5, y // va al entero de abajo: -4. Con positivos parece que «quita decimales», pero con negativos se nota la diferencia.

Míralo paso a paso en Python Tutor

Vamos a ver en directo el or del fragmento 3. Copia esto en Python Tutor, elige Python 3 y avanza línea a línea:

metodo = "tarjeta"

# Versión con el fallo: or devuelve "bizum", que es verdadero
mal = metodo == "efectivo" or "bizum"

# Lo que Python hace por dentro, paso a paso
izquierda = metodo == "efectivo"     # False
mal_2 = izquierda or "bizum"         # False or "bizum" → "bizum"

# Versión correcta: una comparación completa a cada lado
bien = metodo == "efectivo" or metodo == "bizum"

print(mal, mal_2, bien)

Salida:

bizum bizum False

Qué observar paso a paso:

  1. Tras la línea de mal, la variable aparece con el valor "bizum": un str, no un bool. Ese es el síntoma.
  2. izquierda vale False: la comparación con «efectivo» sí se hace, y es falsa.
  3. mal_2 repite la cuenta en dos pasos y vuelve a dar "bizum": False or "bizum" devuelve el segundo operando.
  4. bien vale False, un bool de verdad: con la comparación completa en los dos lados, «tarjeta» no se acepta.
  5. Cambia metodo a "bizum" y vuelve a ejecutar: bien pasa a True y mal sigue valiendo "bizum". Con cualquier método, mal sería igual.

Chuletario: operadores en Python

Lo esencial en 30 segundos: / siempre da float; // es el cociente redondeado hacia abajo y % el resto (y b * (a // b) + a % b == a). Las comparaciones dan bool; != es «distinto de». and y or se paran en cuanto saben la respuesta y devuelven uno de sus operandos. Los decimales no se comparan con ==: céntimos o math.isclose. En caso de duda, paréntesis.

Quiero…CódigoResultado
Dividir con decimales7 / 23.5
Cuántos grupos caben17 // 53
Lo que sobra17 % 52
Las dos cosasdivmod(17, 5)(3, 2)
Potencia y raíz2 ** 10, 9 ** 0.51024, 3.0
Par o imparn % 2 == 0True si es par
Última cifra2847 % 107
Quitar la última cifra2847 // 10284
Céntimos a eurosf"{c // 100},{c % 100:02d} €"10,91 € con 1091
Segundos a h, min, ss // 3600, s % 3600 // 60, s % 601 2 3 con 3723
Distinto deprecio != 100True
Dentro de un rango0 <= nota <= 100True
Comparar decimalesmath.isclose(0.1 + 0.2, 0.3)True
Valor por defectonombre or "Anónimo"'Anónimo' si nombre es ""
Proteger una divisiónb != 0 and a / b > 3sin ZeroDivisionError
Varias opcionesmetodo in ("efectivo", "bizum")True o False

Precedencia (de más a menos): () → ** → signo -x → * / // % → + - → comparaciones → not → and → or. ** se agrupa de derecha a izquierda (2 ** 3 ** 2 = 512); el resto, de izquierda a derecha.

Plantillas de los ejercicios:

EjercicioPlantillaCon los datos de hoy
Tramos sin ifmax(0, meses - 12)3 meses con descuento
Bool como númerocuota - descuento * tiene_descuento2699
Tiempoh, r = divmod(s, 3600); m, s = divmod(r, 60)1, 2, 3
Siguiente nivel(n < 70 and "Notable") or (n < 90 and "Sobresaliente") or "…"'Sobresaliente'
Cifrasn // 10 ** k % 10cifra en la posición k
Cambiopiezas, resto = divmod(resto, valor) en cascada5 piezas para 1257
SíntomaCausa probable
Sale 5.0 en lugar de 5Has usado /; si quieres un entero, //
-7 // 2 da −4 y esperabas −3// redondea hacia abajo; para cortar, int(-7 / 2)
0.1 + 0.2 == 0.3 da FalseDecimales en binario; usa math.isclose o céntimos
La media sale enormeFalta el paréntesis: (a + b + c) / 3
-2 ** 2 da −4** va antes que el signo; escribe (-2) ** 2
Un or acepta cualquier cosax == a or b; escribe x == a or x == b
ZeroDivisionError con un andLa división va antes que la comprobación; cambia el orden
NameError con +=La variable no estaba inicializada (total = 0)

Antes de entregar, pregúntate:

  1. ¿Cada división es la que toca (/ para decimales, // para «cuántos caben»)?
  2. ¿Hay algún == entre dos float? Si lo hay, ¿puedes usar céntimos o math.isclose?
  3. ¿Las fórmulas con suma y multiplicación llevan los paréntesis que harías a mano?
  4. ¿Cada or y cada and tiene una comparación completa a los dos lados?

Y con esto tienes los operadores dominados. El siguiente paso son los condicionales en Python: verás cómo if, elif y else convierten estas comparaciones en decisiones, y cómo el ejercicio 3 queda mucho más legible. Si quieres repasar de dónde venimos, aquí tienes variables y tipos de datos en Python.

Sigue aprendiendoTodos los artículos de Python

¿Te ha servido este artículo? Así puedes apoyar SergioLearns →

Sigue aprendiendo

Deja una respuesta

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