2010-06-10 10 views
301

Me encontré por primera vez con la declaración de Python with. He estado usando Python a la ligera durante varios meses y ¡ni siquiera sabía de su existencia! Dada su condición de algo oscuro, pensé que valdría la pena preguntarse:¿Para qué está diseñada la declaración "con" de python?

  1. ¿Cuál es el pitón with comunicado diseñado para ser utilizado para?
  2. ¿Para qué lo usa ?
  3. ¿Hay algún problema con que deba conocer o antipatrones comunes asociados con su uso? ¿En los casos donde es mejor usar try..finally que with?
  4. ¿Por qué no se usa más ampliamente?
  5. ¿Qué clases de biblioteca estándar son compatibles con él?
+1

Sólo para que conste, [es aquí 'with'] (https://docs.python.org/3/reference/compound_stmts.html#with) en la documentación de Python 3. – Alexey

Respuesta

312
  1. Creo que esto ya ha sido respondido por otros usuarios antes que yo, así que solo lo agrego para completarlo: la declaración with simplifica el manejo de excepciones al encapsular tareas comunes de preparación y limpieza en el llamado context managers. Más detalles se pueden encontrar en PEP 343. Por ejemplo, la declaración open es un administrador de contexto en sí mismo, que le permite abrir un archivo, mantenerlo abierto siempre que la ejecución se encuentre en el contexto de la declaración with donde lo usó, y cerrarlo tan pronto como salga del contexto, no importa si lo ha dejado debido a una excepción o durante el flujo de control regular. La instrucción with se puede utilizar de forma similar a RAII pattern en C++: algún recurso se adquiere mediante la instrucción with y se libera cuando sale del contexto with.

  2. Algunos ejemplos son: apertura de archivos utilizando with open(filename) as fp:, la adquisición de cerraduras usando with lock: (donde lock es una instancia de threading.Lock). También puede construir sus propios administradores de contexto usando el decorador contextmanager de contextlib. Por ejemplo, a menudo utilizar esta opción cuando tengo que cambiar el directorio actual temporalmente y luego regresar a donde estaba:

    from contextlib import contextmanager 
    import os 
    
    @contextmanager 
    def working_directory(path): 
        current_dir = os.getcwd() 
        os.chdir(path) 
        try: 
         yield 
        finally: 
         os.chdir(current_dir) 
    
    with working_directory("data/stuff"): 
        # do something within data/stuff 
    # here I am back again in the original working directory 
    

    He aquí otro ejemplo que redirige temporalmente sys.stdin, sys.stdout y sys.stderr a algún otro identificador de archivo y los restaura después:

    from contextlib import contextmanager 
    import sys 
    
    @contextmanager 
    def redirected(**kwds): 
        stream_names = ["stdin", "stdout", "stderr"] 
        old_streams = {} 
        try: 
         for sname in stream_names: 
          stream = kwds.get(sname, None) 
          if stream is not None and stream != getattr(sys, sname): 
           old_streams[sname] = getattr(sys, sname) 
           setattr(sys, sname, stream) 
         yield 
        finally: 
         for sname, stream in old_streams.iteritems(): 
          setattr(sys, sname, stream) 
    
    with redirected(stdout=open("/tmp/log.txt", "w")): 
        # these print statements will go to /tmp/log.txt 
        print "Test entry 1" 
        print "Test entry 2" 
    # back to the normal stdout 
    print "Back to normal stdout again" 
    

    y, por último, otro ejemplo que crea una carpeta temporal y lo limpia al salir del contexto:

    from tempfile import mkdtemp 
    from shutil import rmtree 
    
    @contextmanager 
    def temporary_dir(*args, **kwds): 
        name = mkdtemp(*args, **kwds) 
        try: 
         yield name 
        finally: 
         shutil.rmtree(name) 
    
    with temporary_dir() as dirname: 
        # do whatever you want 
    
+13

Gracias por agregando la comparación a RAII. Como programador de C++ que me contó todo lo que necesitaba saber. –

+0

Bien, déjame aclarar esto. ¿Estás diciendo que la declaración 'with' está diseñada para llenar una variable con datos hasta que se completen las instrucciones debajo y luego liberar la variable? – Musixauce3000

+0

Porque lo usé para abrir un script py. 'con open ('myScript.py', 'r') como f: pass'. Esperaba poder llamar a la variable 'f' para ver el contenido de texto del documento, ya que esto es lo que aparecería si el documento se asignara a' f' a través de una declaración 'open' regular:' f = open (' myScript.py '). read() '. Pero en su lugar obtuve lo siguiente: '<_io.TextIOWrapper name = 'myScript.py' mode = 'r' encoding = 'cp1252'>'. Qué significa eso? – Musixauce3000

8

Consulte PEP 343 - The 'with' statement, hay una sección de ejemplo al final.

... nueva declaración "con" el lenguaje Python para hacer posible factorizar el uso estándar del try/finally declaraciones.

73

Yo sugeriría dos interesantes conferencias:

  • PEP 343 El "con" Declaración
  • Effbot La comprensión de Python de "con" declaración

1. La declaración with se usa para envolver la ejecución de un blo ck con métodos definidos por un administrador de contexto. Esto permite que los patrones de uso try...except...finally comunes se encapsulen para una reutilización conveniente.

2. Se podría hacer algo como:

with open("foo.txt") as foo_file: 
    data = foo_file.read() 

O

from contextlib import nested 
with nested(A(), B(), C()) as (X, Y, Z): 
    do_something() 

O (Python 3,1)

with open('data') as input_file, open('result', 'w') as output_file: 
    for line in input_file: 
    output_file.write(parse(line)) 

O

lock = threading.Lock() 
with lock: 
    # Critical section of code 

3. No veo ningún Antipattern aquí.
Citando Dive into Python:

try..finally es bueno. con es mejor

4. supongo que está relacionado con el hábito de los programadores utilizar try..catch..finally declaración de otros idiomas.

+3

Realmente se destaca cuando se trata de enhebrar objetos de sincronización. Relativamente raro en Python, pero cuando los necesitas, realmente necesitas 'con'. – detly

+1

diveintopython.org está fuera de servicio (¿permanentemente?). Reflejado en http://www.diveintopython.net/ – snuggles

+0

Ejemplo de una buena respuesta, el archivo abierto es un excelente ejemplo que muestra detrás de las escenas de la apertura, io, el cierre del archivo las operaciones se ocultan limpiamente con un nombre de referencia personalizado – Mayhem

3

los puntos 1, 2 y 3 están razonablemente bien cubiertos:

4: es relativamente nuevo, sólo disponible en python2.6 + (o python2.5 usando from __future__ import with_statement)

33

La instrucción Python with es un soporte de lenguaje incorporado de la lengua Resource Acquisition Is Initialization comúnmente utilizada en C++. Está destinado a permitir la adquisición y liberación segura de los recursos del sistema operativo.

La instrucción with crea recursos dentro de un alcance/bloque. Usted escribe su código usando los recursos dentro del bloque. Cuando el bloque sale, los recursos se liberan limpiamente independientemente del resultado del código en el bloque (es decir, si el bloque sale normalmente o debido a una excepción).

Muchos recursos en la biblioteca de Python que obedecen el protocolo requerido por la declaración with y, por lo tanto, se pueden usar con la versión de fábrica. Sin embargo, cualquiera puede crear recursos que se puedan utilizar en una sentencia with implementando el protocolo bien documentado: PEP 0343

Úselo siempre que adquiera recursos en su aplicación que deben abandonarse explícitamente, como archivos, conexiones de red, bloqueos y similares. .

23

Un ejemplo de un antipatrón podría ser utilizar la with dentro de un bucle cuando sería más eficaz tener la with fuera del bucle

por ejemplo

for row in lines: 
    with open("outfile","a") as f: 
     f.write(row) 

vs

with open("outfile","a") as f: 
    for row in lines: 
     f.write(row) 

La primera forma es abrir y cerrar el archivo para cada row que puede causar problemas de rendimiento en comparación con el s De manera económica, abre y cierra el archivo solo una vez.

19

De nuevo, para completar, agregaré mi caso de uso más útil para las declaraciones with.

Hago mucha informática científica y para algunas actividades necesito la biblioteca Decimal para cálculos de precisión arbitrarios. Parte de mi código necesito alta precisión y para la mayoría de las otras partes necesito menos precisión.

volví mi precisión predeterminada en un número bajo y luego utilice with para obtener una respuesta más precisa para algunas secciones:

from decimal import localcontext 

with localcontext() as ctx: 
    ctx.prec = 42 # Perform a high precision calculation 
    s = calculate_something() 
s = +s # Round the final result back to the default precision 

Yo uso esta mucho con la hipergeométrica prueba que requiere la división de los grandes números forma factoriales resultantes. Cuando haces cálculos de escala genómica, debes tener cuidado con los errores de redondeo y desbordamiento.

0

Otro ejemplo de apoyo fuera de la caja, y que podría ser un poco desconcertante al principio, cuando estás acostumbrado a la forma integrada de open() comporta, son connection objetos de módulos de bases de datos populares, tales como:

El connection objetos son gestores de contexto y, como tal, se pueden utilizar fuera de la caja en un with-statement, sin embargo cuando se utiliza la nota anterior que:

Cuando se termina la with-block, ya sea con una excepción o sin, la conexión no está cerrada. En caso de que el with-block finalice con una excepción, la transacción se retrotrae, de lo contrario la transacción se compromete.

Esto significa que el programador tiene que tener cuidado de cerrar la conexión a sí mismos, sino que permite adquirir una conexión, y utilizarlo en múltiples with-statements, como se muestra en la psycopg2 docs:

conn = psycopg2.connect(DSN) 

with conn: 
    with conn.cursor() as curs: 
     curs.execute(SQL1) 

with conn: 
    with conn.cursor() as curs: 
     curs.execute(SQL2) 

conn.close() 

En el En el ejemplo anterior, observará que los objetos cursor de psycopg2 también son administradores de contexto. De la documentación pertinente sobre el comportamiento:

Cuando un cursor sale del with-block se cierra, liberando cualquier recurso con el tiempo asociado a él. El estado de la transacción no se ve afectado.

0

Todas las soluciones posibles se han enumerado en las respuestas anteriores.

hago una palabra clave exhaustiva cheatsheet para su referencia:

Keywords_33=[('File_2', ['with', 'as']), 
      ('Module_2', ['from', 'import']), 
      ('Constant_3', {'bool': ['False', 'True'], 
          'none': ['None']}), 
      ('Operator_4', {'boolean_operation': {'or', 'and', 'not'}, 
          'comparison': {'is'}}), 
      ('Sequnce_operation_2', ['in', 'del']), 
      ('Klass_1', ['class']), 
      ('Function_7',['lambda', 'def', 'pass', 
          'global', 'nonlocal', 
          'return', 'yield']), 
      ('Repetition_4', ['while', 'for', 'continue', 'break']), 
      ('Condition_3', ['if', 'elif', 'else']), 
      ('Debug_2', ['assert', 'raise']), 
      ('Exception_3', ['try', 'except', 'finally'])] 
0

en Python general “con” sentencia se utiliza para abrir un archivo, el proceso de los datos presentes en el archivo, y también para cerrar la archivo sin llamar a un método close(). La declaración "with" simplifica el manejo de excepciones al proporcionar actividades de limpieza.

forma general de con:

with open(“file name”, “mode”) as file-var: 
    processing statements 

nota: hay necesidad de cerrar el archivo llamando a close() en el archivo-var.close()

Cuestiones relacionadas