2012-07-07 15 views
9

No tengo la intención de simplemente perder el tiempo, pero: se le ha ocurrido a usted también, mientras usa la declaración de with de Python que es realmente contrario a la 5ª línea de "The Zen of Python "que dice" Flat es mejor que anidado "? ¿Puede algún gurú iluminado de Python compartirme algunas de sus ideas sobre esto?zen de Python vs con declaración - reflexionando filosóficamente

(siempre encuentro que un nivel más de hendidura aparece en mi código cada vez que utilizo en lugar de withf.close() ... y no es como si no voy uso try: ... finally: ... de todos modos y por lo tanto los beneficios de with fijas eludirme, aun cuando sea grande para recibir y entender Python más y más ...)


@glglgl (lo siento, no puedo encontrar una manera de escribir código en los comentarios): sí, pero si usted va a la forma with, su código se convierte en:

try: 
    with file(...) as f: 
     ... 
except IOError: 
    ... 

... y el uso sin el try es lo que las personas terminan haciendo en el tipo de código "de un solo uso" en el que usan f.close() en lugar de hacerlo de todos modos (lo cual es malo porque el archivo no puede cerrarse si se lanza una excepción antes de su f.close()), por lo que para el código "hacky" la gente simplemente no usa with porque, no sé, supongo que lo encuentran demasiado "elegante" y para código bien estructurado no trae cualquier beneficio de todos modos, por lo que me parece que no hay un caso de uso del mundo real para él ... realmente fue sobre lo que reflexioné.

+1

La mayor parte del tiempo dejo que IOError se propague y lo atrape en otro lugar. – Dikei

+0

Por más que estoy en desacuerdo con la premisa de esta pregunta, tengo que darle apoyo para hacer que regrese y vuelva a leer PEP [342] (http: //www.python.org/dev/peps/pep-0342 /) y [343] (http://www.python.org/dev/peps/pep-0343/) – kojiro

Respuesta

5

Sí, The Zen of Python afirma "Flat es mejor que anidado", sin embargo, no es la única característica que nos importa; también dice "Simple es mejor que complejo". La belleza de with es que realmente se adhiere a tanto de esos principios como explicaré a continuación.

Cada vez que se encuentre en una reflexión filosófica sobre una función en Python, probablemente valga la pena buscar el Python Enhancement Proposals (PEPs) para leer sobre la motivación detrás de la función. En este caso PEP 343 -- The "with" Statement dice que en la delantera en abstracto:

Este PEP añade una nueva declaración "con" el lenguaje Python para que sea posible factorizar el uso estándar del try/finally declaraciones.

El desglose de las declaraciones try/finally hace que el código sea más simple y legible.

PEP 343 va más allá de proporcionar un azúcar sintáctico simplista, sin embargo. Se establece un protocolo gestor de contexto:

La expresión que sigue inmediatamente a la con la palabra clave en la declaración es una "expresión de contexto" como la expresión proporciona la clave principal como para el entorno de ejecución del gestor de contexto establece para el duración del cuerpo de la declaración.

Utilizando el protocolo de administrador de contexto, los escritores de API pueden ayudar a ocultar la complejidad y asegurar la adquisición/liberación correcta de los recursos en un contexto de subprocesos múltiples.

Pero la verdadera belleza de la declaración with se muestra en el ejemplo 12 de PEP 343 lo que explica que:

Un gerente "anidada" contexto que anida automáticamente los suministrados contextos de izquierda a derecha para evitar sangría excesiva.

Mediante el administrador de nested() contexto se puede tomar el código que se parece a esto:

with a as x: 
    with b as y: 
     with c as z: 
      # Perform operation 

y convertirlo en esto:

with nested(a, b, c) as (x, y, z): 
      # Perform operation 

Tenga en cuenta que nested() se introdujo en Python 2.5, pero a partir de la versión 2.7 está en desuso en favor de esta forma sintáctica del administrador de contexto múltiple:

with a as x, b as y, c as z: 
      # Perform operation 

Claramente, no solo es más simple y más fácil de leer, sino que es mucho más plano que anidado. Por lo tanto, el uso de with es seguir el camino de 無爲 :)

ACTUALIZACIÓN: En respuesta a comments on Simeon Visser's answer aquí es un ejemplo de cuándo se puede utilizar varios gestores de contexto para abrir más de un archivo a la vez, cuando se quiere comprimir el contenido de dos (o más) archivos juntos de tal manera que si la apertura de uno de los archivos falla que hará que todo el asunto no y cierra correctamente cada archivo que se abrió:

from itertools import izip 
with open("/etc/passwd") as a, open("/etc/group") as b, open("/etc/shadow") as c: 
    for lines in izip(a,b,c): 
     print map(lambda x: x.split(':')[0], lines) 

Ejecutar este ejemplo dos veces; una vez como raíz y una vez como usuario normal. Suponiendo que guarde este archivo como ziptogether.py primero intente invocarlo como root con sudo python ziptogether.py y tendrá éxito, pero lo invocará como usuario normal con python ziptogether.py fallará porque no tiene permisos para leer /etc/shadow. Cuando falla, el administrador de contexto se asegurará de que los archivos que se abrieron con éxito antes de la falla se cierren correctamente cuando la ejecución se salga del alcance de la declaración with.

+1

felicitaciones. respuesta hermosa e informativa! – NeuronQ

+1

@NeuronQ Actualicé mi respuesta para señalar que 'anidado()' está en desuso y hay una forma sintáctica aún más simple para múltiples administradores de contexto. – aculich

7

lo mencionas ya: Es más limpio que hacer

f = file(...) 
try: 
    # do work on file 
finally: 
    f.close() 

que simplemente cerrar después de las operaciones de archivo - los cuales no se alcanzaría si se produce una excepción.

Si compara el try/finally con el with, tiene el mismo nivel de sangría, por lo que no pierde nada. Sin embargo, si realiza el manejo de excepciones, tiene un nivel más de indentación, que de hecho está en contra de dicho punto Zen.

OTOH, with encapsula cosas y hace que su uso sea más fácil y más legible, que son otros aspectos Zen.

Me parece imposible seguir siempre todos los aspectos Zen; a veces tienes que pesar uno contra el otro. En este caso, "pierde" un nivel de sangría, pero obtiene una mejor legibilidad y capacidad de mantenimiento. El último parece ser una ventaja para mí.

+0

Intenté responderte pero no encontré la manera de escribir código en los comentarios, así que agregué mi respuesta al pie de mi pregunta – NeuronQ

+0

@ NeuronQ Actualicé la respuesta – glglgl

+0

Acepto: los elementos del Zen de Python con frecuencia deben intercambiarse entre sí. En el presente caso, argumentaría que el único nivel de anidación que debe invertir le permite seguir ** Explicit es mejor que implícito **: Cuando ve un 'with', es explícito que se producirá el manejo final (en lugar de dependiendo del destino de la ejecución de la lógica). "Explicit is better than implicit" es el número 2 en la lista Zen of Python, por lo que * debe * prevalecer a menudo menos nesting. Todo está bien. –

7

Tenga en cuenta que el Zen de Python también dice:

Simple es mejor que complejo.

Complejo es mejor que complicado.

y

legibilidad cuenta.

El uso de un gestor de contexto en la declaración with proporciona varias cosas:

  • comportamiento correcto que el archivo está siempre cerrado
  • legibilidad (with open(..) as f es bastante comprensible)

Puede No apunte a un elemento en el Zen de Python y argumente que todo el código de Python debe satisfacer todos los elementos en todo momento. Por ejemplo, si el nivel mínimo de sangría para resolver un problema en particular de una manera legible y correcta es cuatro, entonces que así sea: si un nivel de sangrado de tres hace que el código sea menos legible, simplemente deje el código solo (cuatro es bueno).

+0

Lo que dices es muy, muy general ... Tienes razón en el punto de "hacer cumplir el comportamiento correcto", ya que sería bueno para el código hacky rápido, pero encontré que exactamente las personas que escriben "código rápido de hacky" don usar la declaración con. Y la parte de legibilidad es subjetiva (me parece que un nivel más de sangría es menos legible, y cuando haces un "múltiples archivos con" como "con abrir (...) como archivo1, abre (...) como archivo3, abre (. ..) como archivo4: "termina siendo muy ilegible porque tiene que desplazarse horizontal o feo porque divide la declaración con en varias líneas. – NeuronQ

+0

¿Hay una buena razón para que se abran cuatro archivos al mismo tiempo? Normalmente puedo lea los datos de los archivos de entrada secuencialmente y lo mismo para escribir los datos en uno o más archivos de salida. Aunque puedo imaginar que hay situaciones en las que se necesitan cuatro archivos. –

+0

@NeuronQ: No veo nada feo al romper el comando en más líneas, especialmente cuando las partes 'open (...) as fx' están alineadas. Estoy de acuerdo con Simeon en que no es el caso típico. De todos modos, es legible de la misma manera que si los cuatro consecuentes' fX = open (. ..) 'se usaron los comandos. – pepr

1
"Flat is better than nested" 

Bueno, lo que es plana?

import thirdparty 
print "I Like Pie!" 

vs

import org.example.thirdparty.something 
System.out.println("I Like Cake") 

etc ...

El Zen de Python no se limita a imponer un límite muesca en su código. Lo alienta a escribir código legible (y por lo tanto, mejor). Si su declaración with está en una función a la que solo pueden acceder 3 capas de objetos (etc., one.two.three.func()), entonces es un problema.

De lo contrario, tres niveles de sangría son tan buenos como cualquier otro.

1

La razón para preferir la with es que no es necesario emparejar manualmente las operaciones relacionadas (como open(...)/.close(), pero el constructo with es más general - no sólo para trabajar con archivos). Esto es importante, especialmente en los casos en que la segunda operación no se puede ejecutar debido a las razones que no son claramente visibles desde el código fuente. Usted le está diciendo a la máquina que se ocupe de mí, y la máquina es mejor en el caso que en el humano. De esta forma puedes librar al grupo de errores desagradables que pueden ser difíciles de encontrar.

Por cierto, debe usar open(...) en lugar del file(...). Python 3 no sabe nada sobre el file(...) y, de lo contrario, tendrá que corregir el código más adelante.

+0

De hecho, tal vez debería explorar el uso de 'with' con otras cosas que no sean archivos, realmente nunca pensé en esto. Sí, 'abrir (...)' es lo que uso, no sé dónde estaba mi mente cuando escribí 'archivo (...)' pero afortunadamente no afectó el significado de la pregunta. – NeuronQ

Cuestiones relacionadas