Las variables temporales pueden mejorar la lectura cuando nombran una idea importante. Pero también pueden agregar ruido, esconder errores o crear estados intermedios difíciles de seguir.
En este tema aprenderemos a distinguir variables útiles de variables innecesarias. También veremos cómo simplificar cálculos, nombrar resultados intermedios con intención y evitar estados que cambian demasiadas veces dentro de una función.
Una variable temporal es útil cuando le da nombre a una idea del dominio o evita repetir una expresión compleja.
subtotal = calcular_subtotal(productos)
descuento = obtener_descuento(cliente)
total_con_descuento = subtotal * (1 - descuento)
En este caso, los nombres explican etapas del cálculo. No son ruido: ayudan a leer el flujo.
Una variable temporal estorba cuando solo repite una expresión obvia o se usa una sola vez sin mejorar la intención.
def obtener_nombre_completo(nombre, apellido):
resultado = f"{nombre} {apellido}"
return resultado
La variable resultado no agrega información. Podemos simplificar:
def obtener_nombre_completo(nombre, apellido):
return f"{nombre} {apellido}"
Una variable temporal con nombre genérico suele empeorar la lectura.
def calcular_total(productos):
x = sum(producto["precio"] * producto["cantidad"] for producto in productos)
y = x * 1.21
z = y + 1500
return z
La función tiene pasos, pero los nombres no dicen nada. Una mejora sería:
def calcular_total(productos):
subtotal = sum(
producto["precio"] * producto["cantidad"]
for producto in productos
)
total_con_iva = subtotal * 1.21
total_con_envio = total_con_iva + 1500
return total_con_envio
Cuando una misma variable cambia muchas veces, el lector debe recordar qué representa en cada momento.
def calcular_total(productos, cliente):
total = 0
for producto in productos:
total += producto["precio"] * producto["cantidad"]
if cliente == "vip":
total = total * 0.85
total = total * 1.21
total = total + 1500
return round(total, 2)
La variable total primero es subtotal, luego total con descuento, luego total con impuesto y luego total con envío. Conviene nombrar las etapas importantes.
Una versión más clara puede separar estados:
def calcular_total(productos, cliente):
subtotal = calcular_subtotal(productos)
total_con_descuento = aplicar_descuento(subtotal, cliente)
total_con_iva = total_con_descuento * 1.21
total_con_envio = total_con_iva + 1500
return round(total_con_envio, 2)
No eliminamos todas las variables. Elegimos nombres que muestran cómo progresa el cálculo.
Una variable bandera puede indicar una condición, pero a veces complica el flujo.
def tiene_productos_validos(productos):
encontrado = False
for producto in productos:
if producto["cantidad"] > 0:
encontrado = True
return encontrado
Podemos retornar apenas encontramos el caso:
def tiene_productos_validos(productos):
for producto in productos:
if producto["cantidad"] > 0:
return True
return False
O usar any si resulta claro:
def tiene_productos_validos(productos):
return any(producto["cantidad"] > 0 for producto in productos)
any y all pueden reducir variables temporales y bucles manuales.
def todos_tienen_precio(productos):
return all(producto["precio"] > 0 for producto in productos)
Pero no conviene forzar expresiones demasiado largas. Si la condición crece, extrae una función con nombre.
def es_producto_valido(producto):
return producto["precio"] > 0 and producto["cantidad"] > 0
def todos_son_validos(productos):
return all(es_producto_valido(producto) for producto in productos)
A veces dejamos variables creadas durante la depuración y luego olvidamos eliminarlas.
def calcular_subtotal(productos):
cantidad_productos = len(productos)
subtotal = sum(
producto["precio"] * producto["cantidad"]
for producto in productos
)
return subtotal
Si cantidad_productos no se usa, debe eliminarse. Ruff puede detectar este caso.
python -m ruff check src tests
Un acumulador explícito puede ser correcto, pero a veces una expresión con sum comunica mejor la intención.
def calcular_subtotal(productos):
subtotal = 0
for producto in productos:
subtotal += producto["precio"] * producto["cantidad"]
return subtotal
Versión alternativa:
def calcular_subtotal(productos):
return sum(
producto["precio"] * producto["cantidad"]
for producto in productos
)
Ambas pueden ser válidas. La segunda es más compacta, pero la primera puede ser preferible si luego hay reglas adicionales dentro del bucle.
A veces creamos una lista intermedia solo para sumarla o contarla.
def calcular_total_productos_activos(productos):
valores = []
for producto in productos:
if producto["activo"]:
valores.append(producto["precio"] * producto["cantidad"])
return sum(valores)
Podemos usar una expresión generadora:
def calcular_total_productos_activos(productos):
return sum(
producto["precio"] * producto["cantidad"]
for producto in productos
if producto["activo"]
)
No hay que eliminar variables por regla automática. Si una expresión es compleja, una variable con buen nombre ayuda.
def puede_recibir_descuento(usuario, compra):
es_cliente_habilitado = usuario["activo"] and not usuario["bloqueado"]
supera_monto_minimo = compra["total"] >= 10000
tiene_email = usuario["email"] != ""
return es_cliente_habilitado and supera_monto_minimo and tiene_email
Estas variables temporales son útiles porque nombran reglas del dominio.
En ventas_demo, revisa si calcular_total_venta tiene estados intermedios claros. Una versión razonable podría ser:
def calcular_total_venta(productos, cliente, pais):
subtotal = calcular_subtotal(productos)
descuento = obtener_descuento(cliente)
impuesto = obtener_impuesto(pais)
total_con_descuento = subtotal * (1 - descuento)
total_con_impuesto = total_con_descuento * (1 + impuesto)
total_con_envio = aplicar_envio(total_con_impuesto)
return round(total_con_envio, 2)
Los nombres muestran la evolución del cálculo. Si todas las etapas se llamaran total, sería más difícil revisar errores.
Eliminar variables temporales puede cambiar comportamiento si se altera el orden de operaciones. Ejecuta pruebas antes y después.
python -m pytest
También puedes agregar pruebas específicas para funciones simplificadas:
def test_calcular_subtotal():
productos = [
{"precio": 1000, "cantidad": 2},
{"precio": 500, "cantidad": 3},
]
assert calcular_subtotal(productos) == 3500
Simplifica el siguiente código:
def obtener_emails_activos(usuarios):
resultado = []
for usuario in usuarios:
activo = usuario["activo"]
email = usuario["email"]
if activo == True:
if email != "":
resultado.append(email)
salida = resultado
return salida
Una posible mejora es:
def obtener_emails_activos(usuarios):
emails = []
for usuario in usuarios:
if usuario["activo"] and usuario["email"] != "":
emails.append(usuario["email"])
return emails
También podríamos usar una comprensión de listas si el equipo la considera clara.
def obtener_emails_activos(usuarios):
return [
usuario["email"]
for usuario in usuarios
if usuario["activo"] and usuario["email"] != ""
]
Esta versión es compacta y clara para muchos programadores Python. Si la condición fuera más compleja, convendría extraer una función de consulta.
Mejora esta función sin cambiar el comportamiento:
def calcular_importe(items):
total = 0
valores = []
for item in items:
precio = item["precio"]
cantidad = item["cantidad"]
valor = precio * cantidad
valores.append(valor)
total = sum(valores)
resultado = round(total, 2)
return resultado
Realiza estas tareas:
sum con una expresión generadora.python -m ruff check src tests
python -m pytest
Antes de continuar, verifica que puedes hacer lo siguiente:
any o all.En este tema vimos que las variables temporales no son buenas o malas por sí mismas. Algunas aclaran la intención; otras agregan ruido o esconden estados confusos.
En el próximo tema estudiaremos el manejo de errores: excepciones silenciadas, genéricas o mal ubicadas.