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 un float, aunque ambos operandos sean enteros: 6 / 3 da 2.0, no 2.
  • // da el resultado redondeado hacia abajo (la "división entera"). Si ambos son enteros, da int. Si alguno es float, da float.
>>> 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: 3.5-3.5 redondea hacia 4-4, no hacia 3-3. 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:

  1. Probar paridad: numero % 2 == 0 significa "número par".
  2. Convertir segundos a minutos: segundos // 60 = minutos completos, segundos % 60 = segundos sobrantes.
  3. Recorrer ciclos: índice de un día de la semana = dia_total % 7.

Identidad útil: a=(a//b)b+(a%b)a = (a // b) \cdot b + (a % b). Verificalo: 13=25+313 = 2 \cdot 5 + 3. 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 3.03.0 (raíz cuadrada), 2 ** -1 da 0.50.5 (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 / 22). 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, 7mod2=1-7 \mod 2 = 1. En C, da 1-1. En Python, da 11 — 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:

  1. Paréntesis ( ) — siempre primero.
  2. Potencia ** — derecha-asociativa: 2 ** 3 ** 2 = 2 ** 9 = 512, no 64.
  3. Negación unaria -x.
  4. Multiplicación, división, módulo: *, /, //, %.
  5. Suma, resta: +, -.
  6. Comparaciones: <, <=, >, >=, ==, !=.
  7. Negación lógica not.
  8. and.
  9. 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 TrueTrue; cualquier FalseFalse
or "o" (al menos uno verdadero) cualquier TrueTrue; False or FalseFalse
not "no" (invierte) not TrueFalse; not FalseTrue
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 X siempre da False — no se evalúa X.
  • True or X siempre da True — no se evalúa X.

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.

✏️ 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.

✏️ 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).

✏️ 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))

✏️ Ejercicio 4.5 — Distancia entre dos puntos

Pedile al usuario coordenadas (x1,y1)(x_1, y_1) y (x2,y2)(x_2, y_2) y calculá la distancia euclidiana:

d=(x2x1)2+(y2y1)2d = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2}

4.10 Para profundizar


Definiciones nuevas: operador, expresión, precedencia, asociatividad, división entera, módulo, cortocircuito, mini-lenguaje de formato.