Introducción
Contenido
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 text
y, 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:
Te puede interesar:Introducción a MySQL y Pythonclass 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:
Te puede interesar:Funciones de Lambda en Pythonclass 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,
Te puede interesar:Las mejores bibliotecas de ciencia de datos en Python- 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 elwith
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.
Te puede interesar:Python para PNL: Introducción a la biblioteca StanfordCoreNLPfrom 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 yield
s 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 elreturn
declaración, va antes de latry
bloquear aquí: básicamente las instrucciones para abrir el recurso. - En lugar de devolver el recurso,
yield
eso, dentro de untry
bloquear. - El contenido del
__exit__
el método va dentro del correspondientefinally
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:
- https://docs.python.org/3/reference/compound_stmts.html#with
- https://docs.python.org/3/reference/datamodel.html#context-managers
- https://docs.python.org/3/library/contextlib.html
- https://rszalski.github.io/magicmethods/