Audiencia. Personas que ya han visto algo de programación y quieren repasar Python de forma práctica.
Prerrequisitos. Funciones, cadenas, diccionarios y manejo básico de iterables.
Duración orientativa. 60–90 minutos, incluida la práctica.
Objetivos de aprendizaje
- Mostrar y formatear resultados con
print. - Separar la lectura interactiva de la lógica de negocio.
- Leer y escribir texto con
pathliby gestores de contexto. - Intercambiar datos tabulares y estructurados con CSV y JSON.
Itinerario
- Salida estándar
- Entrada
- Rutas y texto
- CSV
- JSON
- Flujos
- Práctica
Cómo trabajar: ejecuta las celdas en orden, predice el resultado antes de verlo y modifica los ejemplos. Cada bloque termina con un ejercicio, un punto de partida y una solución posible.
1. Salida estándar
print convierte los argumentos a texto. sep controla el separador y end el final. Para resultados reutilizables, prefiere devolver datos desde una función y deja la presentación en otra capa.
nombre, puntos = "Mina", 93.456
print("Usuario", nombre, "puntos", f"{puntos:.1f}", sep=" | ")
print("Procesando", end="… ")
print("listo")
Usuario | Mina | puntos | 93.5 Procesando… listo
Las f-strings permiten ancho, alineación, relleno, porcentaje, separadores de miles y fechas. Mantienen cerca el valor y su formato.
ventas = [("Norte", 12540.5), ("Sur", 987.25), ("Este", 6340.0)]
for zona, importe in ventas:
print(f"{zona:<8} {importe:>12,.2f} €")
Norte 12,540.50 € Sur 987.25 € Este 6,340.00 €
2. Entrada interactiva
input(mensaje) siempre devuelve una cadena. En un notebook no queremos bloquear la ejecución esperando teclado, así que aislamos la conversión en una función y le inyectamos una fuente de entrada simulada para probarla.
def pedir_entero(mensaje, leer=input):
texto = leer(mensaje)
return int(texto)
def entrada_simulada(mensaje):
print(mensaje + "42")
return "42"
respuesta = pedir_entero("Escribe un entero: ", leer=entrada_simulada)
print("Doble:", respuesta * 2)
Escribe un entero: 42 Doble: 84
3. Rutas y archivos de texto
pathlib.Path compone rutas de forma portable. Especifica encoding="utf-8". Para que el curso no deje basura, los ejemplos trabajan en un directorio temporal que se elimina al final.
from pathlib import Path
import tempfile
_directorio_temporal = tempfile.TemporaryDirectory()
carpeta = Path(_directorio_temporal.name)
ruta_texto = carpeta / "notas.txt"
ruta_texto.write_text("Primera línea\nSegunda línea: ñ y €\n", encoding="utf-8")
contenido = ruta_texto.read_text(encoding="utf-8")
print(contenido)
Primera línea Segunda línea: ñ y €
Para archivos grandes, recorre línea a línea. El gestor with cierra el recurso incluso si ocurre un error. Los modos habituales son r (leer), w (reemplazar), a (añadir) y b (binario).
with ruta_texto.open("a", encoding="utf-8") as archivo:
archivo.write("Tercera línea\n")
with ruta_texto.open("r", encoding="utf-8") as archivo:
lineas_limpias = [linea.strip() for linea in archivo]
print(lineas_limpias)
['Primera línea', 'Segunda línea: ñ y €', 'Tercera línea']
4. CSV: datos tabulares
El módulo csv conoce comillas, delimitadores y saltos de línea. DictWriter y DictReader conectan naturalmente con diccionarios. Abre con newline="" para que el módulo gestione los finales de línea.
import csv
ruta_csv = carpeta / "personas.csv"
personas = [
{"nombre": "Ana", "edad": 31, "ciudad": "Lugo"},
{"nombre": "Leo", "edad": 27, "ciudad": "León"},
]
with ruta_csv.open("w", encoding="utf-8", newline="") as archivo:
escritor = csv.DictWriter(archivo, fieldnames=["nombre", "edad", "ciudad"])
escritor.writeheader()
escritor.writerows(personas)
with ruta_csv.open("r", encoding="utf-8", newline="") as archivo:
leidas = list(csv.DictReader(archivo))
print(leidas) # CSV devuelve texto; convierte tipos cuando sea necesario.
[{'nombre': 'Ana', 'edad': '31', 'ciudad': 'Lugo'}, {'nombre': 'Leo', 'edad': '27', 'ciudad': 'León'}]
5. JSON: datos estructurados
JSON admite objetos, listas, cadenas, números, booleanos y null. En Python corresponden aproximadamente a dict, list, str, int/float, bool y None. No preserva tuplas ni clases personalizadas por sí solo.
import json
configuracion = {
"tema": "oscuro",
"notificaciones": True,
"idiomas": ["es", "en"],
"limite": None,
}
ruta_json = carpeta / "config.json"
ruta_json.write_text(
json.dumps(configuracion, ensure_ascii=False, indent=2),
encoding="utf-8",
)
recuperada = json.loads(ruta_json.read_text(encoding="utf-8"))
print(ruta_json.read_text(encoding="utf-8"))
assert recuperada == configuracion
{
"tema": "oscuro",
"notificaciones": true,
"idiomas": [
"es",
"en"
],
"limite": null
}
6. Flujos en memoria
Muchas APIs aceptan un objeto parecido a un archivo. io.StringIO permite probar escritura y lectura sin tocar disco. Para bytes existe io.BytesIO.
from io import StringIO
flujo = StringIO()
print("línea uno", file=flujo)
print("línea dos", file=flujo)
texto_generado = flujo.getvalue()
print(repr(texto_generado))
'línea uno\nlínea dos\n'
Errores habituales
- Confiar en que
inputdevuelve un número sin convertir ni validar. - Abrir texto sin declarar codificación.
- Construir rutas concatenando
/o\; usaPath. - Usar
evalpara leer datos: ejecuta código arbitrario. Prefiere JSON. - Suponer que CSV conserva tipos.
Extensión opcional: prueba argparse para programas de línea de comandos y tomllib para configuración TOML.
Práctica · Informe de contactos
Escribe crear_informe(contactos, ruta) que guarde un JSON UTF-8 con:
- el total de contactos;
- las ciudades únicas ordenadas;
- los propios contactos.
Después, vuelve a leerlo y comprueba el resultado.
def crear_informe(contactos, ruta):
# TODO: construye el informe, escríbelo y devuélvelo.
pass
Ver una solución posible
import json
def crear_informe(contactos, ruta):
informe = {
"total": len(contactos),
"ciudades": sorted({c["ciudad"] for c in contactos}),
"contactos": contactos,
}
ruta.write_text(
json.dumps(informe, ensure_ascii=False, indent=2),
encoding="utf-8",
)
return informe
contactos = [
{"nombre": "Ana", "ciudad": "Lugo"},
{"nombre": "Eva", "ciudad": "Lugo"},
{"nombre": "Leo", "ciudad": "León"},
]
ruta_informe = carpeta / "informe.json"
esperado = crear_informe(contactos, ruta_informe)
leido = json.loads(ruta_informe.read_text(encoding="utf-8"))
print(leido)
assert leido == esperado and leido["total"] == 3
{'total': 3, 'ciudades': ['León', 'Lugo'], 'contactos': [{'nombre': 'Ana', 'ciudad': 'Lugo'}, {'nombre': 'Eva', 'ciudad': 'Lugo'}, {'nombre': 'Leo', 'ciudad': 'León'}]}
# Limpieza del directorio temporal creado para los ejemplos.
_directorio_temporal.cleanup()
print("Recursos temporales eliminados")
Recursos temporales eliminados
Comprobación rápida
- [ ] Sé separar entrada, lógica y presentación.
- [ ] Puedo leer y escribir texto UTF-8 con
Path. - [ ] Distingo cuándo usar CSV y cuándo JSON.