Operadores y expresiones
"El programa que sale bien al primer intento es sospechoso." — proverbio de programadores.
Qué vas a aprender en este capítulo
Vas a aprender a calcular con Python. No solo sumar y restar — también dividir bien, sacar el resto, elevar a una potencia, comparar valores, combinar condiciones. Vas a entender por qué 1 / 2 no da 0 en Python (sí en otros lenguajes), por qué 0.1 + 0.2 da algo raro, y cómo escribir expresiones que un humano pueda leer.
4.1 Operadores aritméticos
📐 Fundamento
| Operador | Nombre | Ejemplo | Resultado |
|---|---|---|---|
+ |
suma | 3 + 4 |
7 |
- |
resta | 10 - 3 |
7 |
* |
multiplicación | 5 * 6 |
30 |
/ |
división (real) | 7 / 2 |
3.5 |
// |
división entera (suelo) | 7 // 2 |
3 |
% |
módulo (resto) | 7 % 2 |
1 |
** |
potencia | 2 ** 10 |
1024 |
-x |
negación | -7 |
-7 |
Las dos divisiones de Python. Esta es la diferencia más confusa para quienes vienen de otros lenguajes:
/siempre da unfloat, aunque ambos operandos sean enteros:6 / 3da2.0, no2.//da el resultado redondeado hacia abajo (la "división entera"). Si ambos son enteros, daint. Si alguno esfloat, dafloat.
>>> 7 / 2
3.5
>>> 7 // 2
3
>>> 7.0 // 2
3.0 # float, no int, porque uno era float
>>> -7 // 2
-4 # ¡cuidado! redondea hacia menos infinito
Ese último caso sorprende. // es división suelo (floor), no truncamiento: redondea hacia , no hacia . Java y C truncan; Python redondea al piso. Lo justo y lo natural varían — solo conocelo.
El módulo %. Es el "resto" de la división. Se usa muchísimo:
>>> 13 % 5
3 # 13 = 2*5 + 3
>>> 100 % 4
0 # 100 es múltiplo de 4
>>> 17 % 2
1 # 17 es impar
>>> 18 % 2
0 # 18 es par
Tres usos clásicos del módulo:
- Probar paridad:
numero % 2 == 0significa "número par". - Convertir segundos a minutos:
segundos // 60= minutos completos,segundos % 60= segundos sobrantes. - Recorrer ciclos: índice de un día de la semana =
dia_total % 7.
Identidad útil: . Verificalo: . Por eso son inseparables.
Potencia **. Doble asterisco. Útil para fórmulas:
import math
radio = 5
area = math.pi * radio ** 2 # area = π · r²
print(area) # 78.5398...
** también acepta exponentes fraccionarios y negativos: 9 ** 0.5 da (raíz cuadrada), 2 ** -1 da (recíproco).
⚠️ Trampa común
/ versus // en código viejo. Si encontrás código de Python 2, / puede comportarse como // cuando ambos son enteros (5 / 2 → 2). En Python 3 ya no — / siempre es real. Si te aparece un comportamiento raro, comprobá la versión.
Multiplicar por cero accidental. En la fórmula del precio total con descuento:
descuento = 0
precio_final = precio * (1 - descuento) # bien
precio_final = precio - precio * descuento # también bien
precio_final = precio * descuento # ¡incorrecto! da 0 si descuento=0
El error de un signo entre paréntesis cambia un programa por completo. Los buenos editores tienen tests para esto; los humanos cuidadosos también.
Mod con negativos. En matemáticas formales, . En C, da . En Python, da — el mismo signo que el divisor. Si trabajás con módulos negativos, verificá:
>>> -7 % 2
1
>>> -7 % -2
-1
4.2 Asignación abreviada
Cuando una variable se actualiza usando su valor previo, hay una sintaxis corta que ahorra escritura y deja el código más legible:
| Forma larga | Abreviada | Uso |
|---|---|---|
x = x + 5 |
x += 5 |
acumular |
x = x - 1 |
x -= 1 |
decrementar |
x = x * 2 |
x *= 2 |
duplicar |
x = x / 4 |
x /= 4 |
escalar |
x = x // 3 |
x //= 3 |
división entera |
x = x % 7 |
x %= 7 |
acotar |
x = x ** 2 |
x **= 2 |
elevar al cuadrado |
total = 0
total += precio_queso # total = total + precio_queso
total += precio_curtido
print(total)
Para cadenas: += también funciona como concatenación: mensaje += " mas texto".
4.3 Precedencia y paréntesis
📐 Fundamento
Cuando una expresión tiene varios operadores, Python sigue un orden de precedencia parecido al de matemática:
- Paréntesis
( )— siempre primero. - Potencia
**— derecha-asociativa:2 ** 3 ** 2=2 ** 9=512, no64. - Negación unaria
-x. - Multiplicación, división, módulo:
*,/,//,%. - Suma, resta:
+,-. - Comparaciones:
<,<=,>,>=,==,!=. - Negación lógica
not. and.or.
Dentro del mismo nivel, evalúa de izquierda a derecha (excepto **).
Ejemplo.
2 + 3 * 4 # 14, no 20
(2 + 3) * 4 # 20
2 ** 3 ** 2 # 512 (porque 2 ** (3 ** 2))
-2 ** 2 # -4 (la potencia gana sobre la negación)
(-2) ** 2 # 4
Regla práctica. Cuando dudes, poné paréntesis. La memoria del intérprete tiene espacio de sobra; tu memoria humana, no. Una expresión clara con paréntesis "innecesarios" es mejor que una expresión "elegante" que un compañero (o vos en seis meses) tenga que descifrar.
# Difícil de leer
total = precio * cantidad - descuento + iva * precio * cantidad
# Más claro
subtotal = precio * cantidad
total = subtotal - descuento + iva * subtotal
4.4 Operadores de comparación
📐 Fundamento
Los operadores de comparación devuelven un booleano (True o False). Los vamos a usar todo el tiempo desde el próximo capítulo.
| Operador | Significado | Ejemplo |
|---|---|---|
== |
igual | x == 3 |
!= |
distinto | x != 0 |
< |
menor | edad < 18 |
> |
mayor | nota > 7 |
<= |
menor o igual | cantidad <= 10 |
>= |
mayor o igual | saldo >= 0 |
Comparaciones encadenadas. Python permite escribirlas como en matemática:
edad = 19
if 18 <= edad < 65:
print("Edad activa")
Eso es equivalente a (18 <= edad) and (edad < 65). Otros lenguajes (Java, C) no tienen este lujo — tienen que escribir el and explícito.
Comparar str compara alfabéticamente (orden Unicode):
>>> "manzana" < "pera"
True
>>> "Maria" < "maria"
True # las mayúsculas van antes en ASCII
Comparar tipos distintos. Mezclar int y float está bien (Python convierte). Mezclar int y str da error:
>>> 5 == 5.0
True
>>> 5 < "hola"
TypeError: '<' not supported between instances of 'int' and 'str'
Trampa con float. Por la representación binaria limitada, los floats no siempre son exactamente iguales a lo que parecen:
>>> 0.1 + 0.2 == 0.3
False # ¡lo que!
>>> 0.1 + 0.2
0.30000000000000004
Para comparar floats con tolerancia, no uses ==. Usá:
abs(x - y) < 1e-9 # diferencia menor que un nanito
O, en Python 3.5+, math.isclose(x, y).
4.5 Operadores lógicos
📐 Fundamento
Combinan booleanos para formar condiciones complejas:
| Operador | Significado | Tabla |
|---|---|---|
and |
"y" (ambos verdaderos) | True and True → True; cualquier False → False |
or |
"o" (al menos uno verdadero) | cualquier True → True; False or False → False |
not |
"no" (invierte) | not True → False; not False → True |
edad = 19
ciclo = 1
es_estudiante_nuevo = edad >= 17 and edad < 25 and ciclo == 1
puede_votar = edad >= 18
no_es_menor = not (edad < 18)
Cortocircuito. and y or evalúan de izquierda a derecha y se detienen apenas pueden decidir el resultado:
False and Xsiempre daFalse— no se evalúaX.True or Xsiempre daTrue— no se evalúaX.
Eso es útil para evitar errores:
if x != 0 and 10 / x > 1: # si x es 0, el segundo nunca se evalúa, no hay división por cero
...
Verdad y falsedad de cualquier valor. En Python, los condicionales no necesitan booleanos puros — interpretan cualquier valor como "verdadero" o "falso":
| Valor | Vale como bool |
|---|---|
0, 0.0 |
False |
"" (cadena vacía) |
False |
[], {}, () (colecciones vacías) |
False |
None |
False |
| Todo lo demás | True |
Por eso if nombre: es lo mismo que if nombre != "":. Es un atajo que se ve mucho en código Python.
4.6 f-strings: formatear bien
Repasamos lo del capítulo anterior y agregamos detalles:
nombre = "Maria"
saldo = 1234.5678
items = 7
print(f"Cliente: {nombre}")
print(f"Saldo: ${saldo:.2f}") # dos decimales
print(f"Saldo: ${saldo:,.2f}") # con separador de miles
print(f"Items: {items:5d}") # entero alineado en 5 caracteres
print(f"Nombre: {nombre:>10}") # derecha en 10
print(f"Nombre: {nombre:<10}|") # izquierda en 10
print(f"Nombre: {nombre:^10}|") # centrado en 10
print(f"Ratio: {0.5:.1%}") # como porcentaje
Salida:
Cliente: Maria
Saldo: $1234.57
Saldo: $1,234.57
Items: 7
Nombre: Maria
Nombre: Maria |
Nombre: Maria |
Ratio: 50.0%
El mini-lenguaje de formato:
{valor:[fill][align][sign][#][0][width][,][.precision][type]}
Sin pánico — usás siempre los mismos cuatro o cinco. La cheatsheet:
| Caso | Formato |
|---|---|
| Dinero, dos decimales | :.2f |
| Dinero con miles | :,.2f |
| Porcentaje | :.1% |
| Entero alineado a la derecha en 5 | :5d |
Texto centrado en 10, relleno con - |
:-^10 |
| Ceros a la izquierda | :04d (ej: 0007) |
4.7 Proyecto: total de la pupusería con IVA y descuento
🏗️ Avance del proyecto — Pupusería La Esquina
Ahora que sabemos calcular bien, mejoramos el cálculo del recibo: aceptamos varios productos y aplicamos un descuento si el subtotal supera $10.
# Pupuseria La Esquina - Capitulo 4: cuentas serias
NOMBRE_PUPUSERIA = "Pupuseria La Esquina"
IVA = 0.13
UMBRAL_DESCUENTO = 10.00 # subtotal para activar descuento
PORCENTAJE_DESCUENTO = 0.05 # 5%
precio_queso = 0.50
precio_revuelta = 0.60
precio_chicharron = 0.60
precio_curtido = 0.25
precio_refresco = 1.00
print(f"=== {NOMBRE_PUPUSERIA} ===")
cant_q = int(input("Pupusas de queso: "))
cant_r = int(input("Pupusas revueltas: "))
cant_c = int(input("Pupusas de chicharron: "))
cant_cu = int(input("Curtidos extra: "))
cant_re = int(input("Refrescos: "))
# Subtotales por producto
sub_q = cant_q * precio_queso
sub_r = cant_r * precio_revuelta
sub_c = cant_c * precio_chicharron
sub_cu = cant_cu * precio_curtido
sub_re = cant_re * precio_refresco
subtotal = sub_q + sub_r + sub_c + sub_cu + sub_re
# Descuento si supera el umbral (lo decidimos con un truco que veremos en el cap 5)
descuento = subtotal * PORCENTAJE_DESCUENTO * (subtotal >= UMBRAL_DESCUENTO)
base_imponible = subtotal - descuento
impuesto = base_imponible * IVA
total = base_imponible + impuesto
# Recibo
ancho = 38
print()
print("=" * ancho)
print(f" {NOMBRE_PUPUSERIA}")
print("=" * ancho)
print(f" Queso x{cant_q:>3} ${sub_q:>7.2f}")
print(f" Revuelta x{cant_r:>3} ${sub_r:>7.2f}")
print(f" Chicharron x{cant_c:>3} ${sub_c:>7.2f}")
print(f" Curtido x{cant_cu:>3} ${sub_cu:>7.2f}")
print(f" Refresco x{cant_re:>3} ${sub_re:>7.2f}")
print("-" * ancho)
print(f" Subtotal ${subtotal:>7.2f}")
print(f" Descuento (5%) ${descuento:>7.2f}")
print(f" IVA (13%) ${impuesto:>7.2f}")
print("=" * ancho)
print(f" TOTAL ${total:>7.2f}")
print("=" * ancho)
Truco usado: subtotal * PORCENTAJE_DESCUENTO * (subtotal >= UMBRAL_DESCUENTO). La comparación devuelve True o False, que valen 1 y 0. Multiplicando, "activamos" el descuento o lo dejamos en cero. Es un patrón útil pero turbio. En el próximo capítulo lo escribimos de forma más legible con if/else.
4.8 Resumen visual
| Operador | Para qué |
|---|---|
+ - * / // % ** |
Aritmética. Ojo con / (real) y // (entera). |
+= -= *= etc. |
Actualizar una variable usando su valor previo. |
== != < > <= >= |
Comparar (devuelven bool). |
and or not |
Combinar booleanos. Cortocircuito. |
f"... {x:.2f} ..." |
Formatear. :.2f para dinero. |
| Paréntesis | Cuando duden, paréntesis. |
4.9 Ejercicios
✏️ Ejercicio 4.1 — De segundos a hh:mm:ss
Pedile al usuario un número de segundos y mostralo en formato H:MM:SS. Por ejemplo, 3725 segundos → 1:02:05.
Solución
total = int(input("Segundos: "))
horas = total // 3600
minutos = (total % 3600) // 60
segundos = total % 60
print(f"{horas}:{minutos:02d}:{segundos:02d}")
{minutos:02d} significa "entero, ancho mínimo 2, relleno con ceros a la izquierda".
✏️ Ejercicio 4.2 — Promedio ponderado
Las notas de Programación I tienen tres parciales (cada uno vale 25%) y un proyecto final (vale 25%). Pedile al usuario las cuatro notas y mostrá el promedio.
Solución
p1 = float(input("Parcial 1: "))
p2 = float(input("Parcial 2: "))
p3 = float(input("Parcial 3: "))
pf = float(input("Proyecto final: "))
promedio = 0.25 * p1 + 0.25 * p2 + 0.25 * p3 + 0.25 * pf
# Equivalente:
# promedio = (p1 + p2 + p3 + pf) / 4
print(f"Promedio: {promedio:.2f}")
✏️ Ejercicio 4.3 — Cambio en colones
Imaginá que viajás a Costa Rica con $100. Si $1 = ¢520 colones, ¿cuántos colones recibís? Asumí que el cajero te da en billetes de 1000 y monedas de 100, sin centavos. Calculá:
- Total de colones.
- Cuántos billetes de 1000.
- Cuántas monedas de 100 (después de los billetes).
Solución
TIPO_CAMBIO = 520
dolares = float(input("Dolares: "))
colones_total = int(dolares * TIPO_CAMBIO)
billetes_1000 = colones_total // 1000
restante = colones_total % 1000
monedas_100 = restante // 100
print(f"Total: ¢{colones_total:,}")
print(f" {billetes_1000} billetes de 1000")
print(f" {monedas_100} monedas de 100")
✏️ Ejercicio 4.4 — Predecir el resultado
Sin ejecutar, decí qué imprime cada línea.
print(2 + 3 * 4)
print((2 + 3) * 4)
print(10 / 3)
print(10 // 3)
print(10 % 3)
print(2 ** 10)
print(7 // -2)
print(-7 // 2)
print(7 % -2)
print(True + True + False)
print(2 < 5 < 8)
print(2 < 5 and 5 < 8)
print(not (3 > 5))
Solución
14
20
3.3333333333333335
3
1
1024
-4 # 7 / -2 = -3.5 → suelo es -4
-4 # -7 / 2 = -3.5 → suelo es -4
-1 # 7 = -4 * -2 + (-1)
2 # True+True+False = 1+1+0
True
True
True
✏️ Ejercicio 4.5 — Distancia entre dos puntos
Pedile al usuario coordenadas y y calculá la distancia euclidiana:
Solución
x1 = float(input("x1: "))
y1 = float(input("y1: "))
x2 = float(input("x2: "))
y2 = float(input("y2: "))
dx = x2 - x1
dy = y2 - y1
distancia = (dx ** 2 + dy ** 2) ** 0.5
print(f"Distancia: {distancia:.4f}")
** 0.5 es la raíz cuadrada. También podés usar math.sqrt(...) después de import math. Las dos formas son equivalentes.
4.10 Para profundizar
- Documentación oficial — operadores numéricos: https://docs.python.org/es/3/library/stdtypes.html#numeric-types-int-float-complex
- Tabla completa de precedencia: https://docs.python.org/es/3/reference/expressions.html#operator-precedence
- Próximo capítulo: Condicionales — usar todos estos operadores para que el programa decida qué hacer.
Definiciones nuevas: operador, expresión, precedencia, asociatividad, división entera, módulo, cortocircuito, mini-lenguaje de formato.