Manejo de Archivos y Excepciones¶
En esta sección aprenderemos sobre el manejo de archivos y excepciones en Python, dos conceptos fundamentales para crear programas robustos y trabajar con datos externos.
Manejo de Archivos¶
Python proporciona funciones integradas para trabajar con archivos. Los archivos se pueden abrir para lectura, escritura o ambos.
Apertura de Archivos¶
La función open() se utiliza para abrir archivos. Su sintaxis básica es:
file = open(nombre_archivo, modo)
Modos de Apertura¶
Existen dos categorías principales de modos: texto y binario.
Modos de Texto¶
Los archivos de texto se manejan como cadenas de caracteres (strings):
'r'- Lectura (modo por defecto)'w'- Escritura (crea nuevo archivo o sobrescribe si existe)'a'- Append (añade al final del archivo)'r+'- Lectura y escritura
Modos Binarios¶
Para archivos binarios (imágenes, PDF, etc.), se añade “b” al modo:
'rb'- Lectura binaria'wb'- Escritura binaria'ab'- Append binario'r+b'- Lectura y escritura binaria
Diferencias entre Modo Texto y Binario¶
Modo Texto:
Los datos se manejan como strings
Realiza conversiones de caracteres especiales (\n, \r\n)
Codificación automática (UTF-8 por defecto)
Ideal para archivos .txt, .csv, .py, etc.
# Ejemplo modo texto
with open('archivo.txt', 'r') as f:
texto = f.read() # Lee como string
Modo Binario:
Los datos se manejan como bytes
No realiza conversiones de caracteres
No aplica codificación
Necesario para archivos no textuales
# Ejemplo modo binario
with open('imagen.jpg', 'rb') as f:
datos = f.read() # Lee como bytes
Ejemplos Prácticos¶
# Copiando una imagen (modo binario)
with open('original.jpg', 'rb') as origen:
with open('copia.jpg', 'wb') as destino:
destino.write(origen.read())
# Trabajando con texto (modo texto)
with open('notas.txt', 'w') as f:
f.write('Hola\nMundo') # \n se convierte automáticamente al separador de línea del sistema
# Trabajando con CSV (modo texto)
with open('datos.csv', 'r') as f:
for linea in f:
campos = linea.strip().split(',')
Lectura de Archivos¶
Python ofrece varios métodos para leer archivos, cada uno con sus propias características y casos de uso específicos.
Método read()¶
El método read() lee todo el contenido del archivo como una única cadena de texto:
# Leer todo el archivo de una vez
with open('archivo.txt', 'r') as f:
contenido = f.read()
print(contenido) # Muestra todo el contenido
Ventajas:
Simple y directo
Útil para archivos pequeños
Desventajas:
Puede consumir mucha memoria con archivos grandes
No es eficiente para procesamiento línea por línea
Iteración Línea por Línea¶
Python permite iterar directamente sobre el archivo, leyendo una línea a la vez:
# Leer línea por línea
with open('archivo.txt', 'r') as f:
for linea in f:
# strip() elimina espacios en blanco y saltos de línea
print(linea.strip())
Ventajas:
Eficiente en memoria
Ideal para archivos grandes
Permite procesar el archivo secuencialmente
Desventajas:
No permite acceso aleatorio a las líneas
Método readlines()¶
El método readlines() lee todas las líneas del archivo y las devuelve como una lista:
# Leer todas las líneas en una lista
with open('archivo.txt', 'r') as f:
lineas = f.readlines()
# Cada elemento de la lista es una línea del archivo
for linea in lineas:
print(linea.strip())
Ventajas:
Permite acceso aleatorio a las líneas
Útil cuando necesitas manipular las líneas en cualquier orden
Facilita operaciones como ordenamiento o filtrado
Desventajas:
Consume más memoria que la lectura línea por línea
No recomendado para archivos muy grandes
Ejemplo Práctico Comparativo¶
# Ejemplo que muestra diferentes formas de leer un archivo
def leer_archivo_diferentes_formas(nombre_archivo):
# Método 1: read()
print("Usando read():")
with open(nombre_archivo, 'r') as f:
contenido = f.read()
print(f"Contenido completo: {contenido}\n")
# Método 2: iteración línea por línea
print("Usando iteración línea por línea:")
with open(nombre_archivo, 'r') as f:
for i, linea in enumerate(f, 1):
print(f"Línea {i}: {linea.strip()}")
print()
# Método 3: readlines()
print("Usando readlines():")
with open(nombre_archivo, 'r') as f:
lineas = f.readlines()
print(f"Número total de líneas: {len(lineas)}")
for i, linea in enumerate(lineas, 1):
print(f"Línea {i}: {linea.strip()}")
Consideraciones Adicionales¶
Manejo de Archivos Grandes:
Para archivos grandes, usar iteración línea por línea
Evitar
read()yreadlines()con archivos muy grandes
Codificación:
Especificar la codificación al abrir el archivo si es necesario:
with open('archivo.txt', 'r', encoding='utf-8') as f: contenido = f.read()
Memoria:
read()yreadlines()cargan todo el contenido en memoriaLa iteración línea por línea es más eficiente en memoria
Rendimiento:
La iteración línea por línea es generalmente más rápida para procesamiento secuencial
readlines()es mejor cuando necesitas acceso aleatorio a las líneas
Escritura en Archivos¶
Python proporciona varios métodos para escribir en archivos. Es importante entender las diferentes opciones y sus implicaciones.
Método write()¶
El método write() permite escribir una cadena de texto en el archivo:
# Escribir una cadena simple
with open('archivo.txt', 'w') as f:
f.write('Hola Mundo\n') # \n añade un salto de línea
# Escribir múltiples cadenas con write()
with open('archivo.txt', 'w') as f:
f.write('Primera línea\n')
f.write('Segunda línea\n')
f.write('Tercera línea\n')
Características:
Escribe exactamente lo que se le pasa
No añade automáticamente saltos de línea
Retorna el número de caracteres escritos
Método writelines()¶
El método writelines() permite escribir una secuencia de cadenas:
# Escribir una lista de líneas
lineas = ['Línea 1\n', 'Línea 2\n', 'Línea 3\n']
with open('archivo.txt', 'w') as f:
f.writelines(lineas)
# Usando comprensión de lista para añadir saltos de línea
lineas = ['Línea 1', 'Línea 2', 'Línea 3']
with open('archivo.txt', 'w') as f:
f.writelines(linea + '\n' for linea in lineas)
Características:
Acepta cualquier iterable de strings
No añade separadores automáticamente
Más eficiente para escribir múltiples líneas
Modos de Escritura¶
Modo “w” (write):
Crea un nuevo archivo o sobrescribe si existe
Borra todo el contenido previo
with open('archivo.txt', 'w') as f:
f.write('Nuevo contenido') # Sobrescribe todo el archivo
Modo “a” (append):
Añade contenido al final del archivo
Mantiene el contenido existente
with open('archivo.txt', 'a') as f:
f.write('\nContenido adicional') # Añade al final
Ejemplos Prácticos¶
Escribir datos estructurados:
# Escribir datos en formato específico
datos = [
{'nombre': 'Ana', 'edad': 25},
{'nombre': 'Juan', 'edad': 30}
]
with open('personas.txt', 'w') as f:
for persona in datos:
f.write(f"Nombre: {persona['nombre']}, Edad: {persona['edad']}\n")
Combinar write() y writelines():
with open('reporte.txt', 'w') as f:
# Escribir encabezado
f.write("REPORTE DIARIO\n")
f.write("=============\n\n")
# Escribir contenido
items = ['Item 1', 'Item 2', 'Item 3']
f.writelines(f"- {item}\n" for item in items)
Consideraciones Importantes¶
Manejo de Codificación:
Especificar la codificación al abrir el archivo:
with open('archivo.txt', 'w', encoding='utf-8') as f: f.write('Texto con caracteres especiales: áéíóú')
Buenas Prácticas:
Siempre usar
withpara garantizar que el archivo se cierreVerificar permisos de escritura
Hacer copias de seguridad antes de sobrescribir archivos importantes
Rendimiento:
writelines()es más eficiente para múltiples líneasPara archivos grandes, considerar escribir en bloques
Evitar abrir y cerrar el archivo repetidamente
Plataformas:
Los saltos de línea pueden variar según el sistema operativo
Usar
os.lineseppara compatibilidad multiplataforma
El Administrador de Contexto (with)¶
En Python, existen dos formas principales de trabajar con archivos: usando el administrador de contexto with o manejando manualmente la apertura y cierre de archivos.
Uso del Administrador de Contexto (with)¶
El administrador de contexto with se encarga automáticamente de cerrar el archivo cuando terminamos de usarlo:
# Uso recomendado con with
with open('archivo.txt', 'r') as f:
contenido = f.read()
# Al salir del bloque with, el archivo se cierra automáticamente
Ventajas:
Cierre automático del archivo
Código más limpio y seguro
Manejo automático de recursos
Manejo Manual de Archivos¶
Sin usar with, debemos abrir y cerrar el archivo manualmente:
# Método no recomendado
f = open('archivo.txt', 'r')
contenido = f.read()
f.close() # Debemos recordar cerrar el archivo
Desventajas:
Requiere cerrar el archivo explícitamente
Puede olvidarse cerrar el archivo
Más propenso a errores
Ejemplos Comparativos¶
Lectura Simple:
# Con with (recomendado)
with open('datos.txt', 'r') as archivo:
datos = archivo.read()
# El archivo se cerrará automáticamente
# Sin with (no recomendado)
archivo = open('datos.txt', 'r')
datos = archivo.read()
archivo.close() # No olvidar cerrar
Procesamiento de Múltiples Líneas:
# Con with
with open('numeros.txt', 'r') as archivo:
for linea in archivo:
print(int(linea))
# Archivo se cierra automáticamente
# Sin with
archivo = open('numeros.txt', 'r')
for linea in archivo:
print(int(linea))
archivo.close() # Debemos recordar cerrar
Escritura de Datos:
# Con with
with open('salida.txt', 'w') as archivo:
archivo.write('Línea 1\n')
archivo.write('Línea 2\n')
# Se cierra automáticamente
# Sin with
archivo = open('salida.txt', 'w')
archivo.write('Línea 1\n')
archivo.write('Línea 2\n')
archivo.close() # No olvidar cerrar
Situaciones donde es Necesario el Manejo Manual¶
Aunque with es generalmente la mejor opción, hay situaciones donde el manejo manual puede ser necesario:
Mantener un archivo abierto por largo tiempo:
# Archivo de registro que se mantiene abierto
log_file = open('registro.txt', 'a')
def registrar_evento(evento):
log_file.write(f"{evento}\n")
log_file.flush() # Forzar escritura
# Usar el archivo...
registrar_evento("Inicio de programa")
registrar_evento("Operación completada")
# Cerrar al finalizar el programa
log_file.close()
Múltiples operaciones en diferentes partes del código:
archivo = open('datos.txt', 'r')
def procesar_parte1():
# Leer primeras 10 líneas
for _ in range(10):
print(archivo.readline())
def procesar_parte2():
# Leer las siguientes 5 líneas
for _ in range(5):
print(archivo.readline())
procesar_parte1()
procesar_parte2()
archivo.close()
Buenas Prácticas¶
Usar
withsiempre que sea posibleSi se maneja el archivo manualmente:
Cerrar el archivo tan pronto como sea posible
Usar
flush()cuando sea necesario forzar la escrituraDocumentar por qué se eligió el manejo manual
Considerar el uso de
withen funciones separadas si se necesita procesar el archivo en partes
Manejo de Excepciones¶
¿Qué es una Excepción?¶
Una excepción es un evento que ocurre durante la ejecución de un programa que interrumpe el flujo normal de las instrucciones. En Python, las excepciones son una forma de manejar errores y situaciones inesperadas de manera controlada.
Try-Except Básico¶
La estructura básica try-except permite ejecutar código que podría generar un error y manejar ese error de forma elegante:
# Ejemplo 1: Conversión de tipos
try:
numero = int(input("Ingrese un número: "))
print(f"El número ingresado es: {numero}")
except ValueError:
print("Error: Debe ingresar un número válido")
# Ejemplo 2: División
try:
numerador = 10
denominador = int(input("Ingrese el denominador: "))
resultado = numerador / denominador
print(f"El resultado es: {resultado}")
except ZeroDivisionError:
print("Error: No se puede dividir entre cero")
except ValueError:
print("Error: Debe ingresar un número válido")
Múltiples Excepciones¶
Python permite manejar diferentes tipos de excepciones de manera específica:
def leer_numero_desde_archivo(nombre_archivo):
try:
with open(nombre_archivo, 'r') as archivo:
numero = int(archivo.readline())
resultado = 100 / numero
return resultado
except FileNotFoundError:
print(f"Error: No se encontró el archivo '{nombre_archivo}'")
except ValueError:
print("Error: El contenido del archivo no es un número")
except ZeroDivisionError:
print("Error: El número leído es cero y no se puede dividir")
except Exception as e:
print(f"Error inesperado: {str(e)}")
return None
Try-Except-Else-Finally¶
La estructura completa de manejo de excepciones incluye cuatro bloques:
try: Código que puede generar una excepción
except: Maneja la excepción si ocurre
else: Se ejecuta si no hay excepciones
finally: Se ejecuta siempre, haya o no excepciones
Estructura Básica:
def procesar_archivo(nombre_archivo):
try:
with open(nombre_archivo, 'r') as archivo:
contenido = archivo.read()
except FileNotFoundError:
print("El archivo no existe")
return None
else:
print("Archivo leído exitosamente")
return contenido
finally:
print("Proceso de archivo finalizado")
Orden de Ejecución con Finally¶
El bloque finally está diseñado para ejecutar código de limpieza y se ejecuta SIEMPRE, incluso si hay un return en los bloques anteriores:
def ejemplo_orden_ejecucion():
archivo = None
try:
archivo = open('datos.txt', 'r')
datos = archivo.read()
return datos
except FileNotFoundError:
print("No se encontró el archivo")
return None
finally:
if archivo:
archivo.close()
print("Archivo cerrado")
# Si el archivo existe:
# 1. Abre el archivo
# 2. Lee los datos
# 3. Ejecuta finally y cierra el archivo
# 4. Retorna los datos
# Si el archivo no existe:
# 1. Captura FileNotFoundError
# 2. Imprime mensaje de error
# 3. Ejecuta finally (no hay archivo que cerrar)
# 4. Retorna None
Uso Típico de Finally¶
El bloque finally se usa principalmente para tareas de limpieza:
def procesar_datos():
conexion = None
try:
conexion = abrir_conexion()
return procesar_informacion(conexion)
except ConexionError:
print("Error en la conexión")
return None
finally:
if conexion:
conexion.cerrar() # Siempre cierra la conexión
Consideraciones Importantes¶
El bloque
finallysiempre se ejecuta, incluso si hay returns entry,exceptoelseSe usa principalmente para limpieza de recursos
No debe contener lógica de negocio compleja
Es ideal para:
Cerrar archivos
Cerrar conexiones de red
Liberar recursos del sistema
Registrar fin de operaciones
Excepciones Comunes y Sus Casos de Uso¶
ValueError Ocurre cuando una operación recibe un argumento con el tipo correcto pero valor inapropiado:
def procesar_edad():
try:
edad = int(input("Ingrese su edad: "))
if edad < 0:
raise ValueError("La edad no puede ser negativa")
return edad
except ValueError as e:
print(f"Error: {str(e)}")
return None
TypeError Ocurre cuando se intenta realizar una operación con tipos de datos incompatibles:
def sumar_valores(a, b):
try:
resultado = a + b
return resultado
except TypeError:
print(f"Error: No se pueden sumar {type(a)} y {type(b)}")
return None
# Ejemplos de uso:
print(sumar_valores(5, "3")) # Error
print(sumar_valores(5, 3)) # 8
FileNotFoundError Ocurre cuando se intenta acceder a un archivo que no existe:
def leer_configuracion(archivo_config):
try:
with open(archivo_config, 'r') as f:
return f.read()
except FileNotFoundError:
print(f"El archivo {archivo_config} no existe")
return None
IndexError Ocurre al intentar acceder a un índice que está fuera de rango:
def obtener_elemento(lista, indice):
try:
return lista[indice]
except IndexError:
print(f"Error: El índice {indice} está fuera de rango")
return None
KeyError Ocurre al intentar acceder a una clave que no existe en un diccionario:
def obtener_valor(diccionario, clave):
try:
return diccionario[clave]
except KeyError:
print(f"Error: La clave '{clave}' no existe")
return None
Ejemplos Prácticos Integrados¶
Procesamiento de Archivo con Números
def procesar_archivo_numeros(nombre_archivo):
total = 0
numeros_procesados = 0
try:
with open(nombre_archivo, 'r') as archivo:
for numero_linea, linea in enumerate(archivo, 1):
try:
numero = float(linea.strip())
total += numero
numeros_procesados += 1
except ValueError:
print(f"Advertencia: Línea {numero_linea} no contiene un número válido")
if numeros_procesados == 0:
return 0
return total / numeros_procesados
except FileNotFoundError:
print(f"Error: No se encontró el archivo '{nombre_archivo}'")
return None
finally:
print(f"Se procesaron {numeros_procesados} números válidos")
Validación de Datos de Usuario
def validar_usuario(datos_usuario):
campos_requeridos = ['nombre', 'edad', 'email']
try:
# Verificar campos requeridos
for campo in campos_requeridos:
if campo not in datos_usuario:
raise KeyError(f"Falta el campo {campo}")
# Validar edad
edad = int(datos_usuario['edad'])
if edad < 0 or edad > 120:
raise ValueError("Edad fuera de rango válido")
# Validar email (ejemplo simple)
if '@' not in datos_usuario['email']:
raise ValueError("Email inválido")
return True
except KeyError as e:
print(f"Error de datos: {str(e)}")
except ValueError as e:
print(f"Error de validación: {str(e)}")
except Exception as e:
print(f"Error inesperado: {str(e)}")
return False
Mejores Prácticas¶
Especificidad en las Excepciones
Capturar excepciones específicas en lugar de usar except general
Ordenar las excepciones de más específicas a más generales
# Mal
try:
# código
except Exception as e:
print(e)
# Bien
try:
# código
except ValueError:
print("Error de valor")
except TypeError:
print("Error de tipo")
except Exception as e:
print(f"Error inesperado: {e}")
Uso Apropiado de Finally
Usar finally para limpieza de recursos
No colocar lógica compleja en finally
Recordar que finally se ejecuta antes de cualquier return
Manejo de Recursos
Preferir el uso de “with” para recursos como archivos
Asegurar que los recursos se liberen adecuadamente
Mensajes de Error
Proporcionar mensajes claros y útiles
Incluir información relevante para la depuración
Alcance del Try
Minimizar el código dentro del bloque try
Incluir solo el código que puede generar la excepción específica
Documentación
Documentar las excepciones que puede lanzar una función
Explicar el significado y manejo de cada excepción
Lanzamiento de Excepciones con Raise¶
Python permite lanzar excepciones de forma explícita usando el comando raise. Esto es útil cuando queremos indicar que ha ocurrido un error en nuestro código.
Uso Básico de Raise¶
El comando raise permite lanzar cualquiera de las excepciones incorporadas de Python:
def dividir(a, b):
if b == 0:
raise ValueError("No se puede dividir entre cero")
return a / b
# Uso
try:
resultado = dividir(10, 0)
except ValueError as error:
print(f"Error: {error}")
Casos Comunes de Uso¶
Validación de Datos:
def validar_edad(edad):
if edad < 0:
raise ValueError("La edad no puede ser negativa")
if edad > 120:
raise ValueError("Edad no válida")
return True
# Uso
try:
validar_edad(-5)
except ValueError as error:
print(f"Error: {error}")
Validación de Parámetros:
def procesar_lista(lista):
if not lista:
raise ValueError("La lista no puede estar vacía")
if len(lista) > 100:
raise ValueError("La lista es demasiado larga")
return sum(lista)
Manejo de Estados Inválidos:
def actualizar_saldo(saldo, monto):
if saldo < 0:
raise ValueError("El saldo no puede ser negativo")
if monto < 0:
raise ValueError("El monto no puede ser negativo")
return saldo + monto
Relanzamiento de Excepciones¶
A veces queremos capturar una excepción, hacer algo y luego relanzarla:
def procesar_dato(valor):
try:
numero = int(valor)
if numero < 0:
raise ValueError("Número negativo no permitido")
except ValueError as e:
print(f"Ocurrió un error al procesar {valor}")
raise # Relanza la última excepción
Cuándo Usar Raise¶
Es apropiado usar raise cuando:
Los datos o parámetros no cumplen con los requisitos esperados
El programa está en un estado inválido
No se pueden cumplir las precondiciones de una operación
Se detectan valores fuera de rango
Ejemplos Prácticos¶
Validación de Archivo:
def leer_configuracion(nombre_archivo):
if not nombre_archivo.endswith('.txt'):
raise ValueError("El archivo debe ser .txt")
try:
with open(nombre_archivo, 'r') as archivo:
return archivo.read()
except FileNotFoundError:
raise ValueError(f"No se encontró el archivo {nombre_archivo}")
Validación de Datos de Usuario:
def validar_usuario(nombre, edad):
if not nombre:
raise ValueError("El nombre no puede estar vacío")
try:
edad = int(edad)
if edad < 0 or edad > 120:
raise ValueError("Edad fuera de rango")
except ValueError:
raise ValueError("La edad debe ser un número válido")
return True
Buenas Prácticas¶
Mensajes Claros:
Proporcionar mensajes de error descriptivos
Incluir detalles relevantes en el mensaje
Tipo Apropiado:
Usar el tipo de excepción más específico posible
ValueError para errores de valor
TypeError para errores de tipo
RuntimeError para errores de ejecución generales
Documentación:
Documentar las excepciones que puede lanzar una función
Explicar las condiciones que causan cada excepción