Crear una calculadora es uno de los mejores proyectos para practicar variables, funciones, condiciones, bucles, validación y excepciones. Una primera versión puede sumar dos números, pero el mismo diseño puede crecer hasta incluir historial, porcentajes, potencias, memoria y una interfaz gráfica. La clave está en separar el cálculo de la interacción con el usuario.
Para seguir esta guía conviene conocer la función input(), las funciones en Python, el manejo de excepciones, los números decimales y los bucles.
Definir las operaciones
Empieza con funciones pequeñas. Esto facilita las pruebas y evita repetir lógica:
def sumar(a, b):
return a + b
def restar(a, b):
return a - b
def multiplicar(a, b):
return a * b
def dividir(a, b):
if b == 0:
raise ValueError("No se puede dividir entre cero")
return a / b
La documentación oficial sobre funciones explica parámetros y valores de retorno. Para cálculos decimales exactos, la documentación del módulo Decimal es la referencia principal.
Elegir la operación
OPERACIONES = {
"+": sumar,
"-": restar,
"*": multiplicar,
"/": dividir,
}
Un diccionario permite asociar cada símbolo con una función. Así evitamos una cadena larga de if y podemos añadir operaciones sin modificar todo el programa.
Leer números de forma segura
def leer_numero(mensaje):
while True:
texto = input(mensaje).strip().replace(",", ".")
try:
return float(texto)
except ValueError:
print("Introduce un número válido")
El bucle solo termina cuando el valor puede convertirse. Reemplazar la coma ayuda a usuarios acostumbrados al separador decimal español, aunque una aplicación internacional necesita reglas más completas.
Crear la primera versión
def ejecutar_calculo():
a = leer_numero("Primer número: ")
operador = input("Operación (+, -, *, /): ").strip()
b = leer_numero("Segundo número: ")
funcion = OPERACIONES.get(operador)
if funcion is None:
print("Operación no disponible")
return
try:
resultado = funcion(a, b)
except ValueError as error:
print(f"Error: {error}")
return
print(f"Resultado: {resultado:g}")
:g muestra una representación compacta. Para dinero o medidas con precisión fija puedes utilizar :.2f.
Añadir un menú repetible
def main():
while True:
print("\nCALCULADORA")
print("1. Realizar cálculo")
print("0. Salir")
opcion = input("Elige una opción: ").strip()
if opcion == "0":
print("Hasta pronto")
break
if opcion == "1":
ejecutar_calculo()
else:
print("Opción inválida")
if __name__ == "__main__":
main()
La condición if __name__ == "__main__" permite importar las funciones desde tests sin iniciar el menú.
Potencias y resto
def potencia(a, b):
return a ** b
def resto(a, b):
if b == 0:
raise ValueError("El divisor no puede ser cero")
return a % b
OPERACIONES.update({
"**": potencia,
"%": resto,
})
El símbolo % calcula el resto de una división, no un porcentaje comercial. Para calcular un porcentaje, define una función explícita.
Calcular porcentajes
def porcentaje_de(valor, porcentaje):
return valor * porcentaje / 100
def aplicar_descuento(precio, porcentaje):
return precio - porcentaje_de(precio, porcentaje)
Separar ambas operaciones evita ambigüedad. “20 % de 100” y “aplicar 20 % de descuento a 100” producen resultados relacionados, pero representan acciones distintas.
Usar Decimal para importes
Los floats no representan exactamente todos los decimales:
print(0.1 + 0.2)
# 0.30000000000000004
Para dinero usa Decimal:
from decimal import Decimal, InvalidOperation
def leer_decimal(mensaje):
while True:
texto = input(mensaje).strip().replace(",", ".")
try:
return Decimal(texto)
except InvalidOperation:
print("Importe inválido")
def dividir_decimal(a, b):
if b == 0:
raise ValueError("División entre cero")
return a / b
Crea los valores desde strings para no introducir previamente el error del float.
Guardar un historial
from datetime import datetime
historial = []
def registrar(a, operador, b, resultado):
historial.append({
"fecha": datetime.now().isoformat(timespec="seconds"),
"expresion": f"{a} {operador} {b}",
"resultado": str(resultado),
})
Después del cálculo llama a registrar. Para conservar el historial entre ejecuciones, guárdalo en JSON:
import json
from pathlib import Path
RUTA = Path("historial_calculadora.json")
def guardar_historial():
RUTA.write_text(
json.dumps(historial, indent=2, ensure_ascii=False),
encoding="utf-8",
)
No utilizar eval con la entrada del usuario
Puede parecer cómodo hacer esto:
# No recomendado
resultado = eval(input("Expresión: "))
eval puede ejecutar código arbitrario. Un usuario podría leer archivos, ejecutar comandos o modificar información. Para una calculadora, usa una lista explícita de operaciones o un analizador matemático diseñado para expresiones seguras.
Probar las funciones
import pytest
def test_sumar():
assert sumar(2, 3) == 5
def test_dividir():
assert dividir(10, 2) == 5
def test_dividir_por_cero():
with pytest.raises(ValueError):
dividir(5, 0)
Prueba números positivos, negativos, cero, decimales y valores grandes. Las funciones puras son fáciles de probar porque no llaman directamente a input.
Crear una interfaz con Tkinter
La lógica existente puede reutilizarse en una ventana:
import tkinter as tk
ventana = tk.Tk()
ventana.title("Calculadora")
entrada = tk.Entry(ventana, font=("Arial", 18))
entrada.pack(padx=12, pady=12)
resultado = tk.Label(ventana, text="Resultado")
resultado.pack(pady=8)
ventana.mainloop()
No coloques toda la lógica dentro de los callbacks de los botones. Los callbacks deben leer la interfaz, llamar a las funciones y presentar el resultado.
Calculadora científica
El módulo math ofrece raíces, trigonometría y logaritmos:
import math
def raiz_cuadrada(valor):
if valor < 0:
raise ValueError("No hay raíz real para un valor negativo")
return math.sqrt(valor)
Valida el dominio de cada función. Por ejemplo, el logaritmo real requiere un valor positivo.
Errores frecuentes
Los problemas más comunes son no validar la división entre cero, mezclar strings y números, usar eval, redondear demasiado pronto, almacenar dinero en float y repetir lógica dentro del menú. También es frecuente confundir el símbolo de porcentaje con el operador módulo.
Mejoras posibles
Puedes añadir memoria, conversión de unidades, historial exportable, atajos de teclado, temas visuales, pruebas parametrizadas y una opción para copiar el resultado. Mantén cada función pequeña y documenta las reglas de redondeo.
Conclusión
Una calculadora enseña mucho más que operaciones matemáticas. Obliga a diseñar funciones, validar entradas, manejar errores y separar la interfaz de la lógica. Empieza con cuatro operaciones, crea un diccionario de funciones y evita eval. Después añade Decimal, historial, tests e interfaz gráfica sin romper el núcleo del programa.






