{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "da716284",
   "metadata": {},
   "source": [
    "[← Anterior](04_funciones.ipynb) · [Índice](README.md) · [Siguiente →](06_errores_y_excepciones.ipynb)\n",
    "\n",
    "# 05 · Entrada y salida\n",
    "\n",
    "**Audiencia.** Personas que ya han visto algo de programación y quieren repasar Python de forma práctica.  \n",
    "**Prerrequisitos.** Funciones, cadenas, diccionarios y manejo básico de iterables.  \n",
    "**Duración orientativa.** 60–90 minutos, incluida la práctica.\n",
    "\n",
    "## Objetivos de aprendizaje\n",
    "\n",
    "- Mostrar y formatear resultados con `print`.\n",
    "- Separar la lectura interactiva de la lógica de negocio.\n",
    "- Leer y escribir texto con `pathlib` y gestores de contexto.\n",
    "- Intercambiar datos tabulares y estructurados con CSV y JSON.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ef84f81",
   "metadata": {},
   "source": [
    "## Itinerario\n",
    "\n",
    "1. Salida estándar\n",
    "2. Entrada\n",
    "3. Rutas y texto\n",
    "4. CSV\n",
    "5. JSON\n",
    "6. Flujos\n",
    "7. Práctica\n",
    "\n",
    "> **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.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9315a467",
   "metadata": {},
   "source": [
    "## 1. Salida estándar\n",
    "\n",
    "`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.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "2b2e722c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:50.246645Z",
     "iopub.status.busy": "2026-08-02T22:29:50.246524Z",
     "iopub.status.idle": "2026-08-02T22:29:50.250511Z",
     "shell.execute_reply": "2026-08-02T22:29:50.250189Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Usuario | Mina | puntos | 93.5\n",
      "Procesando… listo\n"
     ]
    }
   ],
   "source": [
    "nombre, puntos = \"Mina\", 93.456\n",
    "print(\"Usuario\", nombre, \"puntos\", f\"{puntos:.1f}\", sep=\" | \")\n",
    "print(\"Procesando\", end=\"… \")\n",
    "print(\"listo\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c7b74b1e",
   "metadata": {},
   "source": [
    "Las f-strings permiten ancho, alineación, relleno, porcentaje, separadores de miles y fechas. Mantienen cerca el valor y su formato.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "76470201",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:50.252157Z",
     "iopub.status.busy": "2026-08-02T22:29:50.252035Z",
     "iopub.status.idle": "2026-08-02T22:29:50.254263Z",
     "shell.execute_reply": "2026-08-02T22:29:50.254016Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Norte       12,540.50 €\n",
      "Sur            987.25 €\n",
      "Este         6,340.00 €\n"
     ]
    }
   ],
   "source": [
    "ventas = [(\"Norte\", 12540.5), (\"Sur\", 987.25), (\"Este\", 6340.0)]\n",
    "for zona, importe in ventas:\n",
    "    print(f\"{zona:<8} {importe:>12,.2f} €\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6eecfa17",
   "metadata": {},
   "source": [
    "## 2. Entrada interactiva\n",
    "\n",
    "`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.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "e333b6c3",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:50.255754Z",
     "iopub.status.busy": "2026-08-02T22:29:50.255649Z",
     "iopub.status.idle": "2026-08-02T22:29:50.257924Z",
     "shell.execute_reply": "2026-08-02T22:29:50.257709Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Escribe un entero: 42\n",
      "Doble: 84\n"
     ]
    }
   ],
   "source": [
    "def pedir_entero(mensaje, leer=input):\n",
    "    texto = leer(mensaje)\n",
    "    return int(texto)\n",
    "\n",
    "def entrada_simulada(mensaje):\n",
    "    print(mensaje + \"42\")\n",
    "    return \"42\"\n",
    "\n",
    "respuesta = pedir_entero(\"Escribe un entero: \", leer=entrada_simulada)\n",
    "print(\"Doble:\", respuesta * 2)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "03b3246d",
   "metadata": {},
   "source": [
    "## 3. Rutas y archivos de texto\n",
    "\n",
    "`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.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "713a8834",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:50.259304Z",
     "iopub.status.busy": "2026-08-02T22:29:50.259198Z",
     "iopub.status.idle": "2026-08-02T22:29:50.262222Z",
     "shell.execute_reply": "2026-08-02T22:29:50.261973Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Primera línea\n",
      "Segunda línea: ñ y €\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from pathlib import Path\n",
    "import tempfile\n",
    "\n",
    "_directorio_temporal = tempfile.TemporaryDirectory()\n",
    "carpeta = Path(_directorio_temporal.name)\n",
    "ruta_texto = carpeta / \"notas.txt\"\n",
    "\n",
    "ruta_texto.write_text(\"Primera línea\\nSegunda línea: ñ y €\\n\", encoding=\"utf-8\")\n",
    "contenido = ruta_texto.read_text(encoding=\"utf-8\")\n",
    "print(contenido)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e427f485",
   "metadata": {},
   "source": [
    "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).\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "d8794cc2",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:50.263706Z",
     "iopub.status.busy": "2026-08-02T22:29:50.263620Z",
     "iopub.status.idle": "2026-08-02T22:29:50.266125Z",
     "shell.execute_reply": "2026-08-02T22:29:50.265888Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['Primera línea', 'Segunda línea: ñ y €', 'Tercera línea']\n"
     ]
    }
   ],
   "source": [
    "with ruta_texto.open(\"a\", encoding=\"utf-8\") as archivo:\n",
    "    archivo.write(\"Tercera línea\\n\")\n",
    "\n",
    "with ruta_texto.open(\"r\", encoding=\"utf-8\") as archivo:\n",
    "    lineas_limpias = [linea.strip() for linea in archivo]\n",
    "\n",
    "print(lineas_limpias)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "58b5c62b",
   "metadata": {},
   "source": [
    "## 4. CSV: datos tabulares\n",
    "\n",
    "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.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "90ce41f1",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:50.267486Z",
     "iopub.status.busy": "2026-08-02T22:29:50.267393Z",
     "iopub.status.idle": "2026-08-02T22:29:50.270223Z",
     "shell.execute_reply": "2026-08-02T22:29:50.269999Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[{'nombre': 'Ana', 'edad': '31', 'ciudad': 'Lugo'}, {'nombre': 'Leo', 'edad': '27', 'ciudad': 'León'}]\n"
     ]
    }
   ],
   "source": [
    "import csv\n",
    "\n",
    "ruta_csv = carpeta / \"personas.csv\"\n",
    "personas = [\n",
    "    {\"nombre\": \"Ana\", \"edad\": 31, \"ciudad\": \"Lugo\"},\n",
    "    {\"nombre\": \"Leo\", \"edad\": 27, \"ciudad\": \"León\"},\n",
    "]\n",
    "\n",
    "with ruta_csv.open(\"w\", encoding=\"utf-8\", newline=\"\") as archivo:\n",
    "    escritor = csv.DictWriter(archivo, fieldnames=[\"nombre\", \"edad\", \"ciudad\"])\n",
    "    escritor.writeheader()\n",
    "    escritor.writerows(personas)\n",
    "\n",
    "with ruta_csv.open(\"r\", encoding=\"utf-8\", newline=\"\") as archivo:\n",
    "    leidas = list(csv.DictReader(archivo))\n",
    "\n",
    "print(leidas)  # CSV devuelve texto; convierte tipos cuando sea necesario.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bf3d1a75",
   "metadata": {},
   "source": [
    "## 5. JSON: datos estructurados\n",
    "\n",
    "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.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "fd7104cb",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:50.271562Z",
     "iopub.status.busy": "2026-08-02T22:29:50.271467Z",
     "iopub.status.idle": "2026-08-02T22:29:50.273833Z",
     "shell.execute_reply": "2026-08-02T22:29:50.273600Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "  \"tema\": \"oscuro\",\n",
      "  \"notificaciones\": true,\n",
      "  \"idiomas\": [\n",
      "    \"es\",\n",
      "    \"en\"\n",
      "  ],\n",
      "  \"limite\": null\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "import json\n",
    "\n",
    "configuracion = {\n",
    "    \"tema\": \"oscuro\",\n",
    "    \"notificaciones\": True,\n",
    "    \"idiomas\": [\"es\", \"en\"],\n",
    "    \"limite\": None,\n",
    "}\n",
    "ruta_json = carpeta / \"config.json\"\n",
    "ruta_json.write_text(\n",
    "    json.dumps(configuracion, ensure_ascii=False, indent=2),\n",
    "    encoding=\"utf-8\",\n",
    ")\n",
    "recuperada = json.loads(ruta_json.read_text(encoding=\"utf-8\"))\n",
    "print(ruta_json.read_text(encoding=\"utf-8\"))\n",
    "assert recuperada == configuracion\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b957e436",
   "metadata": {},
   "source": [
    "## 6. Flujos en memoria\n",
    "\n",
    "Muchas APIs aceptan un objeto parecido a un archivo. `io.StringIO` permite probar escritura y lectura sin tocar disco. Para bytes existe `io.BytesIO`.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "cb952bbd",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:50.275120Z",
     "iopub.status.busy": "2026-08-02T22:29:50.275032Z",
     "iopub.status.idle": "2026-08-02T22:29:50.277029Z",
     "shell.execute_reply": "2026-08-02T22:29:50.276813Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "'línea uno\\nlínea dos\\n'\n"
     ]
    }
   ],
   "source": [
    "from io import StringIO\n",
    "\n",
    "flujo = StringIO()\n",
    "print(\"línea uno\", file=flujo)\n",
    "print(\"línea dos\", file=flujo)\n",
    "texto_generado = flujo.getvalue()\n",
    "print(repr(texto_generado))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1508576",
   "metadata": {},
   "source": [
    "## Errores habituales\n",
    "\n",
    "- Confiar en que `input` devuelve un número sin convertir ni validar.\n",
    "- Abrir texto sin declarar codificación.\n",
    "- Construir rutas concatenando `/` o `\\`; usa `Path`.\n",
    "- Usar `eval` para leer datos: ejecuta código arbitrario. Prefiere JSON.\n",
    "- Suponer que CSV conserva tipos.\n",
    "\n",
    "**Extensión opcional:** prueba `argparse` para programas de línea de comandos y `tomllib` para configuración TOML.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "63b655f3",
   "metadata": {},
   "source": [
    "## Práctica · Informe de contactos\n",
    "\n",
    "Escribe `crear_informe(contactos, ruta)` que guarde un JSON UTF-8 con:\n",
    "\n",
    "- el total de contactos;\n",
    "- las ciudades únicas ordenadas;\n",
    "- los propios contactos.\n",
    "\n",
    "Después, vuelve a leerlo y comprueba el resultado.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "8964efc8",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:50.278317Z",
     "iopub.status.busy": "2026-08-02T22:29:50.278229Z",
     "iopub.status.idle": "2026-08-02T22:29:50.279838Z",
     "shell.execute_reply": "2026-08-02T22:29:50.279626Z"
    },
    "tags": [
     "ejercicio"
    ]
   },
   "outputs": [],
   "source": [
    "def crear_informe(contactos, ruta):\n",
    "    # TODO: construye el informe, escríbelo y devuélvelo.\n",
    "    pass\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "55737d07",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:50.281035Z",
     "iopub.status.busy": "2026-08-02T22:29:50.280951Z",
     "iopub.status.idle": "2026-08-02T22:29:50.283747Z",
     "shell.execute_reply": "2026-08-02T22:29:50.283541Z"
    },
    "tags": [
     "solucion"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'total': 3, 'ciudades': ['León', 'Lugo'], 'contactos': [{'nombre': 'Ana', 'ciudad': 'Lugo'}, {'nombre': 'Eva', 'ciudad': 'Lugo'}, {'nombre': 'Leo', 'ciudad': 'León'}]}\n"
     ]
    }
   ],
   "source": [
    "import json\n",
    "\n",
    "def crear_informe(contactos, ruta):\n",
    "    informe = {\n",
    "        \"total\": len(contactos),\n",
    "        \"ciudades\": sorted({c[\"ciudad\"] for c in contactos}),\n",
    "        \"contactos\": contactos,\n",
    "    }\n",
    "    ruta.write_text(\n",
    "        json.dumps(informe, ensure_ascii=False, indent=2),\n",
    "        encoding=\"utf-8\",\n",
    "    )\n",
    "    return informe\n",
    "\n",
    "contactos = [\n",
    "    {\"nombre\": \"Ana\", \"ciudad\": \"Lugo\"},\n",
    "    {\"nombre\": \"Eva\", \"ciudad\": \"Lugo\"},\n",
    "    {\"nombre\": \"Leo\", \"ciudad\": \"León\"},\n",
    "]\n",
    "ruta_informe = carpeta / \"informe.json\"\n",
    "esperado = crear_informe(contactos, ruta_informe)\n",
    "leido = json.loads(ruta_informe.read_text(encoding=\"utf-8\"))\n",
    "print(leido)\n",
    "assert leido == esperado and leido[\"total\"] == 3\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "cbbbd50f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:50.284918Z",
     "iopub.status.busy": "2026-08-02T22:29:50.284829Z",
     "iopub.status.idle": "2026-08-02T22:29:50.287001Z",
     "shell.execute_reply": "2026-08-02T22:29:50.286798Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Recursos temporales eliminados\n"
     ]
    }
   ],
   "source": [
    "# Limpieza del directorio temporal creado para los ejemplos.\n",
    "_directorio_temporal.cleanup()\n",
    "print(\"Recursos temporales eliminados\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "96cb176b",
   "metadata": {},
   "source": [
    "            ## Comprobación rápida\n",
    "\n",
    "            - [ ] Sé separar entrada, lógica y presentación.\n",
    "- [ ] Puedo leer y escribir texto UTF-8 con `Path`.\n",
    "- [ ] Distingo cuándo usar CSV y cuándo JSON.\n",
    "\n",
    "            [← Anterior](04_funciones.ipynb) · [Volver al índice](README.md) · [Siguiente →](06_errores_y_excepciones.ipynb)\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
