Administradores de contexto de Python

A

Introducción

Una de las características más “oscuras” de Python que casi todos los programadores de Python usan, incluso los principiantes, pero que realmente no comprenden, son los administradores de contexto. Probablemente los hayas visto en forma de with declaraciones, que generalmente se encuentran por primera vez cuando aprende a abrir archivos en Python. Aunque los administradores de contexto parecen un poco extraños al principio, cuando realmente nos sumergimos en ellos, comprendemos la motivación y las técnicas detrás de ellos, obtenemos acceso a una nueva arma en nuestro arsenal de programación. Así que sin más preámbulos, ¡profundicemos en ello!

Motivación: gestión de recursos

Como dijo alguien mucho más sabio que yo, “La necesidad es la madre de la invención”. Para comprender realmente qué es un administrador de contexto y cómo podemos usarlo, primero debemos investigar las motivaciones detrás de él, las necesidades que dieron lugar a esta “invención”.

La principal motivación detrás de los administradores de contexto es la gestión de recursos. Cuando un programa quiere tener acceso a un recurso en la computadora, se lo solicita al sistema operativo y, a su vez, el sistema operativo le proporciona un control para ese recurso. Algunos ejemplos comunes de estos recursos son archivos y puertos de red. Lo que es importante comprender es que estos recursos tienen una disponibilidad limitada, por ejemplo, un puerto de red puede ser utilizado por un solo proceso a la vez y hay una cantidad limitada de puertos disponibles. Entonces, cada vez que abrimos un recurso, debemos recordar cerrarlo, para que el recurso se libere. Pero, lamentablemente, es más fácil decirlo que hacerlo.

La forma más sencilla de lograr una gestión adecuada de los recursos sería llamar al close función después de que hayamos terminado con el recurso. Por ejemplo:

opened_file = open('readme.txt')
text = opened_file.read()
...
opened_file.close()

Aquí estamos abriendo un archivo llamado readme.txt, leyendo el archivo y guardando su contenido en una cadena texty, cuando hayamos terminado, cerramos el archivo llamando al close() método del opened_file objeto. Ahora bien, a primera vista esto puede parecer bien, pero en realidad, no es nada robusto. Si ocurre algo inesperado entre la apertura y el cierre del archivo, provocando que el programa no ejecute la línea que contiene la close declaración, habría una fuga de recursos. Estos eventos inesperados son lo que llamamos exceptions, uno común sería cuando alguien cierra a la fuerza el programa mientras se está ejecutando.

Ahora, la forma correcta de manejar esto sería usando el manejo de excepciones, usando try...else bloques. Mira el siguiente ejemplo:

try:
    opened_file = open('readme.txt')
    text = opened_file.read()
    ...
else:
    opened_file.close()

Python siempre se asegura de que el código en el else bloque se ejecuta, independientemente de cualquier cosa que pueda suceder. Esta es la forma en que los programadores en otros lenguajes manejarían la administración de recursos, pero los programadores de Python obtienen un mecanismo especial que les permite implementar la misma funcionalidad sin todos los estándares. Aquí es donde entran en juego los administradores de contexto.

Implementación de administradores de contexto

Ahora que hemos terminado con la parte más crucial sobre la comprensión de los administradores de contexto, podemos lanzarnos a implementarlos. Para este tutorial, implementaremos un File clase. Es totalmente redundante ya que Python ya proporciona esto, pero sin embargo, será un buen ejercicio de aprendizaje ya que siempre podremos relacionarnos con el File clase que ya está en la biblioteca estándar.

La forma estándar y de “nivel inferior” de implementar un administrador de contexto es definir dos métodos “mágicos” en la clase para la que desea implementar la administración de recursos, __enter__ y __exit__. Si se está perdiendo, pensando, “¿qué es este método mágico? Nunca había oído hablar de esto antes”, bueno, si ha comenzado a hacer programación orientada a objetos en Python, seguramente ya ha encontrado un método mágico, el método __init__.

A falta de mejores palabras, son métodos especiales que puede definir para hacer sus clases más inteligentes o agregarles “magia”. Puede encontrar una buena lista de referencia de todos los métodos mágicos disponibles en Python Aquí.

De todos modos, volviendo al tema, antes de que comencemos a implementar estos dos métodos mágicos, tendremos que entender su propósito. __enter__ es el método que se llama cuando abrimos el recurso, o para decirlo de una manera un poco más técnica, cuando “ingresamos” al contexto de tiempo de ejecución. los with declaración vinculará el valor de retorno de este método al objetivo especificado en el as cláusula de la declaración.

Veamos un ejemplo:

class FileManager:
    def __init__(self, filename):
        self.filename = filename
        
    def __enter__(self):
        self.opened_file = open(self.filename)
        return self.opened_file

Como puede ver, el __enter__ El método es abrir el recurso (el archivo) y devolverlo. Cuando usamos esto FileManager en un with declaración, se llamará a este método y su valor de retorno se vinculará a la variable de destino que mencionó en el as cláusula. Lo he demostrado en el siguiente fragmento de código:

with FileManager('readme.txt') as file:
    text = file.read()

Vamos a desglosarlo parte por parte. En primer lugar, una instancia del FileManager La clase se crea cuando la instanciamos, pasando el nombre de archivo “readme.txt” al constructor. Entonces el with declaración comienza a trabajar en él – llama al __enter__ método de eso FileManager objeto y asigna el valor devuelto al file variable mencionada en el as cláusula. Entonces, dentro del with block, podemos hacer lo que queramos hacer con el recurso abierto.

La otra parte importante del rompecabezas es la __exit__ método. los __exit__ El método contiene código de limpieza que debe ejecutarse después de que hayamos terminado con el recurso, pase lo que pase. Las instrucciones de este método serán similares a las del else bloque que discutimos antes mientras discutíamos el manejo de excepciones. Para reiterar, el __exit__ El método contiene instrucciones para cerrar correctamente el controlador de recursos, de modo que el recurso se libere para su uso posterior por otros programas en el sistema operativo.

Ahora echemos un vistazo a cómo podríamos escribir este método:

class FileManager:
    def __exit__(self. *exc):
        self.opened_file.close()

Ahora, siempre que las instancias de esta clase se utilicen en un with declaración, esto __exit__ se llamará al método antes de que el programa salga del with bloquear, o antes de que el programa se detenga debido a alguna excepción. Ahora veamos el conjunto FileManager clase para que tengamos una idea completa.

class FileManager:
    def __init__(self, filename):
        self.filename = filename
        
    def __enter__(self):
        self.opened_file = open(self.filename)
        return self.opened_file
    
    def __exit__(self, *exc):
        self.opened_file.close()

Bastante simple, ¿verdad? Acabamos de definir las acciones de apertura y limpieza en los respectivos métodos mágicos, y Python se encargará de la gestión de recursos donde sea que se utilice esta clase. Eso me lleva al siguiente tema, las diferentes formas en que podemos usar las clases de administrador de contexto, como esta FileManager clase.

Usar administradores de contexto

No hay mucho que explicar aquí, así que en lugar de escribir párrafos largos, proporcionaré algunos fragmentos de código en esta sección:

file = FileManager('readme.txt')
with file as managed_file:
    text = managed_file.read()
    print(text)
with FileManager('readme.txt') as managed_file:
    text = managed_file.read()
    print(text)
def open_file(filename):
    file = FileManager(filename)
    return file

with open_file('readme.txt') as managed_file:
    text = managed_file.read()
    print(text)

Puedes ver que la clave para recordar es,

  • El objeto pasó al with declaración debe tener __enter__ y __exit__ métodos.
  • los __enter__ El método debe devolver el recurso que se utilizará en el with bloquear.

Importante: Hay algunas sutilezas que dejé fuera, para hacer la discusión al grano. Para conocer las especificaciones exactas de estos métodos mágicos, consulte la documentación de Python Aquí.

Usando contextlib

los Zen de Python—El principio rector de Python como una lista de aforismos — establece que,

Lo simple es mejor que lo complejo.

Para realmente llevar este punto a casa, los desarrolladores de Python han creado una biblioteca llamada contextlib que contienen utilidades relacionadas con los administradores de contexto, como si no simplificaran lo suficiente el problema de la administración de recursos. Voy a demostrar solo uno de ellos brevemente aquí, le recomiendo que consulte los documentos oficiales de Python para obtener más información.

from contextlib import contextmanager

@contextmanager
def open_file(filename):
    opened_file = open(filename)
    try:
        yield opened_file
    finally:
        opened_file.close()

Como el código anterior, simplemente podemos definir una función que yields el recurso protegido en un try declaración, cerrándola en la siguiente finally declaración. Otra forma de entenderlo:

  • Todo el contenido que de otro modo pondría en el __enter__ método, excepto el return declaración, va antes de la try bloquear aquí: básicamente las instrucciones para abrir el recurso.
  • En lugar de devolver el recurso, yield eso, dentro de un try bloquear.
  • El contenido del __exit__ el método va dentro del correspondiente finally bloquear.

Una vez que tengamos tal función, podemos decorarla usando el contextlib.contextmanager decorador y estamos bien.

with open_file('readme.txt') as managed_file:
    text = managed_file.read()
    print(text)

Como puede ver, el decorado open_file La función devuelve un administrador de contexto y podemos usarlo directamente. Esto nos permite lograr el mismo efecto que el de crear el FileManager clase, sin todas las molestias.

Otras lecturas

Si se siente entusiasmado y desea leer más sobre los administradores de contexto, le animo a que consulte los siguientes enlaces:

 

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con tus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad