1. Concepto de gestor de contexto
En Python, cualquier objeto que implemente los métodos __enter__ y __exit__ puede usarse como gestor de contexto. Estos objetos están diseñados para trabajar con la sentencia with, la cual se encarga de invocar automáticamente __enter__ al inicio del bloque y __exit__ al finalizar, incluso si ocurre una excepción.
¿Qué resuelve with?
Imaginemos que necesitamos abrir un archivo, leer su contneido y luego cerrarlo. Sin with, debemos asegurarnos manualmente de cerrar el recurso, incluso ante errores. El patrón típico es:
try:
f = open('datos.txt', 'r', encoding='utf-8')
contenido = f.read()
finally:
if f:
f.close()
Este código es funcional pero verboso y propenso a olvidos. La sentencia with simplifica esta tarea:
with open('datos.txt', 'r', encoding='utf-8') as f:
print(f.read())
Al salir del bloque, el archivo se cierra automáticamente gracias al gestor de contexto implementado por la clase file.
Funcionamiento interno
Cuando ejecutamos with expresion as variable:, Python evalúa la expresión y obtiene un objeto gestor de contexto. Luego:
- Llama al método
__enter__del objeto. - Asigna el valor retornado por
__enter__a la variablevariable. - Ejecuta el bloque de código.
- Al salir, invoca
__exit__pasando información sobre excepciones si las hubiera.
2. Crear un gestor de contexto personalizado
Podemos definir nuestras propias clases que implementen __enter__ y __exit__. Esto es útil para manejar conexiones de red, bases de datos, locks, etc.
class GestorArchivo:
def __init__(self, ruta, modo='r', codificacion='utf-8'):
self.ruta = ruta
self.modo = modo
self.codificacion = codificacion
self.archivo = None
def __enter__(self):
print("Abriendo el archivo...")
self.archivo = open(self.ruta, self.modo, encoding=self.codificacion)
return self.archivo
def __exit__(self, tipo_excepcion, valor_excepcion, traceback):
print("Cerrando el archivo...")
if self.archivo:
self.archivo.close()
# Retornar False para que las excepciones se propaguen (o True para suprimirlas)
return False
# Uso
with GestorArchivo('mensaje.txt', 'w', 'utf-8') as f:
f.write("Hola, mundo desde un gestor personalizado.")
print("Fin del bloque with.")
En el ejemplo anterior, la clase GestorArchivo envuelve la lógica de apertura y cierre. El método __exit__ recibe tres argumentos que describen cualquier excepción ocurrida dentro del bloque.
3. Beneficios clave
- Automatización de limpieza: el recurso se cierra incluso si ocurre un error.
- Código más legible y compacto.
- Reutilización: una vez definida la clase, puede utilizarse en cualquier lugar con
with.