Saber Básico A.1.3: Funciones y reutilización de código. Manipulación de archivos PYC2BACH
- Categorías: Clases2627, PYC2BACH2627
Saber Básico A.1.3: Funciones y reutilización de código. Manipulación de archivos.
🧩 1. Funciones: empaquetar lógica reutilizable
Una función es un bloque de código con nombre propio que encapsula una tarea concreta, recibe unos datos de entrada (parámetros) y opcionalmente devuelve un resultado (return).
def: define la función y su lista de parámetros.- Parámetros con valor por defecto: permiten llamar a la función sin especificar todos los argumentos.
return: devuelve un valor al punto donde se llamó la función y termina su ejecución en ese momento.- Docstring (
"""..."""): documenta qué hace la función, justo debajo de la cabecera.
Python
def calcular_media(valores, decimales=2):
"""Devuelve la media aritmética de una lista de valores numéricos."""
if len(valores) == 0:
return None
return round(sum(valores) / len(valores), decimales)
lecturas = [21.5, 22.0, 21.8, 23.1]
print(f"Media de lecturas: {calcular_media(lecturas)}")
print(f"Media con 4 decimales: {calcular_media(lecturas, decimales=4)}")
📊 Ámbito de las variables (local vs. global)
| Ámbito | Dónde existe | Ejemplo |
|---|---|---|
| Local | Solo dentro de la función donde se crea; desaparece al terminar la función | Un parámetro o una variable creada con = dentro de def |
| Global | En todo el programa, fuera de cualquier función | Una variable creada fuera de cualquier def |
Python
contador_llamadas = 0 # variable global
def registrar_llamada():
global contador_llamadas # sin esto, Python crearía una variable LOCAL nueva
contador_llamadas += 1
registrar_llamada()
registrar_llamada()
print(f"Función llamada {contador_llamadas} veces")
Idea clave: una función que solo lee variables globales no necesita
global; pero si quiere modificar una variable global, tiene que declararlo explícitamente conglobal, o Python entenderá que estás creando una variable local nueva con el mismo nombre.
🔁 2. Por qué reutilizar código en vez de repetirlo
El principio DRY (Don’t Repeat Yourself) dice que la misma lógica no debería estar escrita dos veces en un programa. Si necesitas cambiarla, tener que buscarla y corregirla en varios sitios es una fuente constante de errores.
| Sin función (código repetido) | Con función (reutilizable) |
|---|---|
| El mismo cálculo copiado y pegado varias veces, con pequeñas variaciones | Una sola definición, llamada tantas veces como haga falta |
| Un error de lógica hay que corregirlo en cada copia | Un error de lógica se corrige una sola vez |
| Difícil de probar de forma aislada | Se puede probar la función por separado, con distintos valores de entrada |
Idea clave: una función bien diseñada hace una sola cosa, y la hace bien. Si te cuesta resumir en una frase qué hace tu función, probablemente está haciendo demasiadas cosas a la vez y conviene dividirla en varias funciones más pequeñas.
📁 3. Manipulación de archivos en Python
Un programa muchas veces necesita leer datos que ya existen en un archivo, o guardar resultados para que persistan después de que el programa termine. Python gestiona esto con la función open().
📊 Modos de apertura de un archivo
| Modo | Qué hace | ¿Sobrescribe el contenido? |
|---|---|---|
'r' | Lectura (por defecto). Falla si el archivo no existe | — |
'w' | Escritura. Crea el archivo si no existe | Sí, borra el contenido anterior |
'a' | Añadir (append). Escribe al final del archivo | No, añade al final |
'r+' | Lectura y escritura sobre el archivo existente | No, pero puede sobrescribir por posición |
La forma correcta: with open(...) as f:
with open("lecturas.txt", "r") as archivo:
contenido = archivo.read()
print(contenido)
El bloque with (gestor de contexto) cierra el archivo automáticamente al salir del bloque, incluso si ocurre un error dentro. Abrir un archivo sin with y olvidarse de cerrarlo con archivo.close() es un error clásico: el archivo puede quedar bloqueado o los datos escritos pueden no guardarse realmente en disco.
📊 Métodos de lectura y escritura más habituales
| Método | Qué hace |
|---|---|
f.read() | Devuelve todo el contenido como una única cadena de texto |
f.readline() | Devuelve la siguiente línea del archivo, una cada vez |
f.readlines() | Devuelve una lista con todas las líneas del archivo |
for linea in f: | Recorre el archivo línea a línea, de forma eficiente en memoria |
f.write(texto) | Escribe una cadena de texto en el archivo (no añade salto de línea automáticamente) |
f.writelines(lista) | Escribe una lista de cadenas, una tras otra |
Python
# Escribir varias líneas en un archivo nuevo
temperaturas = [21.5, 22.0, 21.8, 23.1, 19.4]
with open("temperaturas.txt", "w") as archivo:
for temp in temperaturas:
archivo.write(f"{temp}\n")
# Leer el archivo línea a línea y procesarlo
with open("temperaturas.txt", "r") as archivo:
for linea in archivo:
valor = float(linea.strip()) # strip() elimina el salto de línea "\n"
print(f"Lectura procesada: {valor} °C")
Idea clave:
linea.strip()es casi obligatorio al leer archivos de texto línea a línea, porque cada línea leída incluye el salto de línea (\n) del final. Olvidarlo es otro error muy común: convertir"21.5\n"a número falla si no se limpia antes.
🔗 4. Ejemplo trabajado: función + archivo, trabajando juntos
Problema: leer un archivo con lecturas de temperatura de un sensor, calcular la media reutilizando la función calcular_media() del punto 1, y escribir un informe en un archivo nuevo.
Python
def calcular_media(valores, decimales=2):
"""Devuelve la media aritmética de una lista de valores numéricos."""
if len(valores) == 0:
return None
return round(sum(valores) / len(valores), decimales)
def leer_lecturas(nombre_archivo):
"""Lee un archivo de temperaturas (una por línea) y devuelve una lista de floats."""
lecturas = []
with open(nombre_archivo, "r") as archivo:
for linea in archivo:
lecturas.append(float(linea.strip()))
return lecturas
def generar_informe(lecturas, nombre_salida):
"""Calcula estadísticas básicas y las guarda en un archivo de informe."""
media = calcular_media(lecturas)
maximo = max(lecturas)
minimo = min(lecturas)
with open(nombre_salida, "w") as archivo:
archivo.write(f"Número de lecturas: {len(lecturas)}\n")
archivo.write(f"Media: {media} °C\n")
archivo.write(f"Máxima: {maximo} °C\n")
archivo.write(f"Mínima: {minimo} °C\n")
lecturas_sensor = leer_lecturas("temperaturas.txt")
generar_informe(lecturas_sensor, "informe_temperaturas.txt")
print("Informe generado correctamente.")
Fíjate en la estructura: tres funciones pequeñas, cada una con una única responsabilidad, en lugar de un bloque único que lea, calcule y escriba todo de golpe. Si mañana cambia el formato del archivo de entrada, solo hay que tocar leer_lecturas(); el resto del programa no se entera.
⚡ Reto Algorítmico (Evaluación Continua)
Implementa un programa en Python que gestione un registro de accesos:
- Escribe una función
registrar_acceso(usuario, nombre_archivo)que añada (modo'a') una línea al archivoaccesos.logcon el nombre de usuario y no sobrescriba lo que ya había.- Escribe una función
contar_accesos(nombre_archivo)que lea el archivo línea a línea y devuelva un diccionario con cada usuario como clave y el número de veces que aparece como valor.- Usa ambas funciones: registra al menos 5 accesos (con usuarios repetidos) y muestra por pantalla el diccionario resultante, ordenado de mayor a menor número de accesos.
📝 Boletín de Ejercicios Prácticos: Funciones y Manipulación de Archivos
Asignatura: Programación y Computación (2º Bachillerato)
Saber Básico A.1.3: Funciones y reutilización de código. Manipulación de archivos.
🧩 Bloque 1: Funciones y Ámbito de Variables
Ejercicio 1: Validador de Contraseñas Reutilizable
Una aplicación necesita comprobar la fortaleza de contraseñas en varios puntos de su código. Crea una función validar_password(password, longitud_minima=8) que devuelva True si la contraseña cumple todas estas condiciones, y False en caso contrario:
- Longitud mínima (parámetro configurable, 8 por defecto).
- Contiene al menos un dígito.
- Contiene al menos una letra mayúscula.
Después, usa la función para validar tres contraseñas de prueba distintas e imprime el resultado de cada una.
Python
def validar_password(password, longitud_minima=8):
if len(password) < longitud_minima:
return False
tiene_digito = any(c.isdigit() for c in password)
tiene_mayuscula = any(c.isupper() for c in password)
return tiene_digito and tiene_mayuscula
pruebas = ["abc123", "Contraseña1", "SOLOMAYUSCULAS", "seguraX9"]
for p in pruebas:
resultado = "Válida ✅" if validar_password(p) else "No válida ❌"
print(f"{p:20} -> {resultado}")
Ejercicio 2: El error de ámbito
El siguiente código pretende llevar la cuenta de errores detectados en un sistema, pero no funciona como se espera:
total_errores = 0
def registrar_error(mensaje):
total_errores = total_errores + 1
print(f"Error nº{total_errores}: {mensaje}")
registrar_error("Fallo de conexión")
registrar_error("Timeout")
print(f"Total de errores: {total_errores}")
- Ejecuta mentalmente este código. ¿Qué error de Python crees que se produce, y en qué línea?
Respuesta: ____________________________________________________________________ - Explica, con lo que viste en teoría sobre ámbito local y global, por qué ocurre ese error.
Respuesta: ____________________________________________________________________ - Corrige el código para que funcione correctamente:
📁 Bloque 2: Manipulación de Archivos
Ejercicio 3: Generador de Diario de Sesiones
Crea un programa que simule un registro de inicios de sesión.
- Escribe una función
nueva_sesion(usuario, hora, nombre_archivo="sesiones.txt")que añada una línea al archivo con el formatousuario;hora(sin sobrescribir las sesiones anteriores). - Escribe una función
listar_sesiones(nombre_archivo="sesiones.txt")que lea el archivo línea a línea, separe cada línea por el carácter;, y muestre por pantalla un listado con el formato"Usuario: X — Hora: Y". - Registra al menos 4 sesiones de prueba y después llama a
listar_sesiones()para comprobar que se han guardado correctamente.
Python
def nueva_sesion(usuario, hora, nombre_archivo="sesiones.txt"):
with open(nombre_archivo, "a") as archivo:
archivo.write(f"{usuario};{hora}\n")
def listar_sesiones(nombre_archivo="sesiones.txt"):
with open(nombre_archivo, "r") as archivo:
for linea in archivo:
usuario, hora = linea.strip().split(";")
print(f"Usuario: {usuario} — Hora: {hora}")
nueva_sesion("jlusero", "08:15")
nueva_sesion("mgarcia", "08:20")
nueva_sesion("jlusero", "09:45")
nueva_sesion("apereira", "10:02")
listar_sesiones()
Ejercicio 4: El archivo que no se cierra bien
Un compañero ha escrito este código para guardar una lista de temperaturas, pero cuando lo ejecuta varias veces el archivo se queda vacío o con datos incompletos:
archivo = open("temperaturas.txt", "w")
temperaturas = [18.2, 19.5, 20.1]
for t in temperaturas:
archivo.write(str(t))
- ¿Qué dos problemas ves en este código? (pista: uno tiene que ver con cómo se separan los valores al escribirlos, y otro con cómo se gestiona la apertura y cierre del archivo)
Respuesta 1: ____________________________________________________________________
Respuesta 2: ____________________________________________________________________ - Reescribe el código usando
with open(...)y corrigiendo el formato de escritura para que cada temperatura quede en su propia línea:
🚀 Bloque 3: Reto Integrador — Analizador de Logs de Acceso (Nivel Examen / EvAU)
Una empresa guarda los accesos a su servidor en un archivo de texto accesos.log, donde cada línea tiene el formato:
usuario;ip;resultado
Por ejemplo:
jlusero;192.168.1.15;OK
mgarcia;10.0.0.5;FALLO
jlusero;172.16.0.1;OK
apereira;10.0.0.5;FALLO
jlusero;192.168.1.15;OK
mgarcia;10.0.0.5;OK
Crea un script completo, dividido en funciones reutilizables, que realice:
- Una función
crear_log_prueba(nombre_archivo)que genere el archivo de ejemplo anterior (usa el contenido de arriba, línea por línea). - Una función
leer_accesos(nombre_archivo)que lea el archivo y devuelva una lista de diccionarios, uno por línea, con las claves"usuario","ip"y"resultado". - Una función
contar_fallos_por_usuario(accesos)que reciba la lista de diccionarios y devuelva un diccionario con cada usuario como clave y su número de accesos con resultado"FALLO"como valor. - Una función
escribir_informe(conteo, nombre_salida)que escriba en un nuevo archivoinforme_accesos.txtuna línea por cada usuario con al menos un fallo, en el formato:"ALERTA: usuario X tiene N intento(s) fallido(s)". - Llama a las cuatro funciones en orden y muestra por pantalla el contenido final de
informe_accesos.txt.
Python
def crear_log_prueba(nombre_archivo):
lineas = [
"jlusero;192.168.1.15;OK",
"mgarcia;10.0.0.5;FALLO",
"jlusero;172.16.0.1;OK",
"apereira;10.0.0.5;FALLO",
"jlusero;192.168.1.15;OK",
"mgarcia;10.0.0.5;OK",
]
with open(nombre_archivo, "w") as archivo:
for linea in lineas:
archivo.write(linea + "\n")
def leer_accesos(nombre_archivo):
accesos = []
with open(nombre_archivo, "r") as archivo:
for linea in archivo:
usuario, ip, resultado = linea.strip().split(";")
accesos.append({"usuario": usuario, "ip": ip, "resultado": resultado})
return accesos
def contar_fallos_por_usuario(accesos):
conteo = {}
for acceso in accesos:
if acceso["resultado"] == "FALLO":
usuario = acceso["usuario"]
conteo[usuario] = conteo.get(usuario, 0) + 1
return conteo
def escribir_informe(conteo, nombre_salida):
with open(nombre_salida, "w") as archivo:
for usuario, fallos in conteo.items():
archivo.write(f"ALERTA: usuario {usuario} tiene {fallos} intento(s) fallido(s)\n")
# Ejecución del flujo completo
crear_log_prueba("accesos.log")
accesos = leer_accesos("accesos.log")
fallos = contar_fallos_por_usuario(accesos)
escribir_informe(fallos, "informe_accesos.txt")
print("=" * 45)
print(" INFORME DE ACCESOS FALLIDOS ")
print("=" * 45)
with open("informe_accesos.txt", "r") as archivo:
print(archivo.read())
