¿Manejo de excepciones barato en Python?

Leí en una respuesta anterior que el manejo de excepciones es barato en Python, por lo que no deberíamos hacer una verificación precondicional.

No he oído hablar de esto antes, pero soy relativamente nuevo en Python. El manejo de excepciones significa una llamada dinámica y un retorno estático, mientras que una sentencia if es una llamada estática, un retorno estático.

¿Cómo puede ser malo hacer el chequeo y try-except ser bueno, parece ser al revés. ¿Puede alguien explicarme esto?

Puede encontrar útil esta publicación: Probar / Excepto Rendimiento en Python: una prueba simple en la que Patrick Altman realizó algunas pruebas simples para ver cuál es el rendimiento en varios escenarios de verificación precondicional (específica para las claves del diccionario en este caso) y usando solo excepciones . También se proporciona el código si desea adaptarlo para probar otros condicionales.

Las conclusiones a las que llegó:

A partir de estos resultados, creo que es justo determinar rápidamente una serie de conclusiones:

  1. Si existe una alta probabilidad de que el elemento no exista, entonces es mejor que lo compruebe con has_key.
  2. Si no va a hacer nada con la Excepción si se genera, entonces es mejor que no coloque uno.
  3. Si es probable que el elemento exista, entonces hay una muy pequeña ventaja de usar un bloque try / except en lugar de usar has_key, sin embargo, la ventaja es muy leve.

No te preocupes por las cosas pequeñas. Ya has elegido uno de los lenguajes de script más lentos, por lo que intentar optimizar el código de operación no te va a ayudar mucho. La razón para elegir un lenguaje dynamic interpretado como Python es optimizar su tiempo, no la CPU.

Si usa expresiones idiomáticas comunes, verá todos los beneficios de la creación rápida de prototipos y el diseño limpio, y su código se ejecutará más rápido a medida que se publiquen nuevas versiones de Python y se actualice el hardware de la computadora.

Si tiene problemas de rendimiento, perfile su código y optimice sus algoritmos lentos. Pero mientras tanto, use excepciones para situaciones excepcionales, ya que hará que cualquier refactorización que haga en última instancia sea mucho más fácil.

Dejando a un lado las medidas de rendimiento que otros han dicho, el principio guía a menudo se estructura como “es más fácil pedir perdón que pedir permiso” en lugar de “mirar antes de saltar”.

Considere estos dos fragmentos:

 # Look before you leap if not os.path.exists(filename): raise SomeError("Cannot open configuration file") f = open(filename) 

contra

 # Ask forgiveness ... try: f = open(filename) except IOError: raise SomeError("Cannot open configuration file") 

¿Equivalente? Realmente no. Los sistemas operativos son sistemas de toma múltiple. ¿Qué sucede si el archivo se eliminó entre la prueba de ‘existe’ y la llamada ‘abierta’?

¿Qué pasa si el archivo existe pero no es legible? Qué pasa si es un nombre de directorio en lugar de un archivo. Puede haber muchos modos de falla posibles y verificarlos todos es mucho trabajo. Sobre todo porque la llamada “abierta” ya verifica e informa de todos los posibles fallos.

La guía debería ser reducir la posibilidad de un estado inconsistente, y la mejor manera de hacerlo es usar excepciones en lugar de prueba / llamada.

“¿Puede alguien explicarme esto?”

Depende.

Aquí hay una explicación, pero no es útil. Su pregunta proviene de sus suposiciones. Dado que el mundo real entra en conflicto con sus suposiciones, debe significar que sus suposiciones son erróneas. No hay mucha explicación, pero es por eso que lo preguntas.

“El manejo de excepciones significa una llamada dinámica y un retorno estático, mientras que una sentencia if es una llamada estática, un retorno estático”.

¿Qué significa “llamada dinámica”? Buscando marcos de stack para un controlador? Supongo que eso es lo que estás hablando. Y una “llamada estática” es de alguna manera ubicar el bloque después de la instrucción if.

Quizás esta “llamada dinámica” no sea la parte más costosa de la operación. Tal vez la evaluación de la expresión if-statement es un poco más cara que la simple prueba “prueba y falla”.

Resulta que las comprobaciones de integridad interna de Python son casi las mismas que las de su statement if, y deben hacerse de todos modos. Como Python’s siempre va a verificar, su statement if es (en su mayoría) redundante.

Puede leer sobre el manejo de excepciones de bajo nivel en http://docs.python.org/c-api/intro.html#exceptions .


Editar

Más al punto: el debate de si vs. excepto, no importa.

Como las excepciones son baratas, no las etiquete como un problema de rendimiento.

Use lo que hace que su código sea claro y significativo . No pierdas el tiempo en micro-optimizaciones como esta.

Con Python, es fácil comprobar diferentes posibilidades de velocidad: conozca el módulo timeit :

… sesión de ejemplo (usando la línea de comando) que compara el costo de usar hasattr () frente a try / except para probar los atributos de objetos presentes y faltantes.

 % timeit.py 'try:' ' str.__nonzero__' 'except AttributeError:' ' pass' 100000 loops, best of 3: 15.7 usec per loop % timeit.py 'if hasattr(str, "__nonzero__"): pass' 100000 loops, best of 3: 4.26 usec per loop % timeit.py 'try:' ' int.__nonzero__' 'except AttributeError:' ' pass' 1000000 loops, best of 3: 1.43 usec per loop % timeit.py 'if hasattr(int, "__nonzero__"): pass' 100000 loops, best of 3: 2.23 usec per loop 

Estos resultados de tiempo se muestran en el caso de hasattr() , generar una excepción es lento, pero realizar una prueba es más lento que no generar la excepción. Por lo tanto, en términos de tiempo de ejecución, usar una excepción para manejar casos excepcionales tiene sentido.

EDITAR: La opción de línea de comando -n predeterminará un conteo suficientemente grande para que el tiempo de ejecución sea significativo. Una cita del manual :

Si no se da -n, se calcula un número adecuado de bucles probando potencias sucesivas de 10 hasta que el tiempo total sea de al menos 0.2 segundos.

Soy un principiante de python también. Si bien no puedo decir por qué exactamente el manejo de Excepciones ha sido llamado barato en el contexto de esa respuesta, aquí están mis pensamientos:

Tenga en cuenta que verificar con if-elif-else tiene que evaluar una condición cada vez. El manejo de excepciones, incluida la búsqueda de un controlador de excepciones, ocurre solo en condiciones excepcionales, lo que probablemente sea raro en la mayoría de los casos. Esa es una clara ganancia de eficiencia. Como lo señaló Jay, es mejor usar la lógica condicional en lugar de las excepciones cuando existe una alta probabilidad de que la clave esté ausente. Esto se debe a que si la clave está ausente la mayor parte del tiempo, no es una condición excepcional.

Dicho esto, sugiero que no te preocupes por la eficiencia y más bien por el significado. Utilice el manejo de excepciones para detectar casos excepcionales y condiciones de verificación cuando desee decidir sobre algo. S. Lott me recordó la importancia del significado ayer.

Caso en punto:

 def xyz(key): dictOb = {x:1, y:2, z:3} #Condition evaluated every time if dictOb.has_key(key): #Access 1 to dict print dictOb[key] #Access 2 

Versus

 #Exception mechanism is in play only when the key isn't found. def xyz(key): dictOb = {x:1, y:2, z:3} try: print dictOb[key] #Access 1 except KeyError: print "Not Found" 

En general, tener algún código que maneje algo, como una clave faltante, solo en caso de que necesite un manejo excepcional, pero en situaciones como cuando la clave no está presente la mayor parte del tiempo, lo que realmente quiere hacer es decidir si la clave es presente => if-else. Python enfatiza y anima a decir lo que quieres decir.

Por qué se prefieren las excepciones a if-elif ->

  1. Expresa el significado más claramente cuando busca condiciones excepcionales o inesperadas en su código.
  2. Es más limpio y mucho más legible.
  3. Es más flexible.
  4. Se puede utilizar para escribir código más conciso.
  5. Evita un montón de cheques desagradables.
  6. Es más mantenible.

Nota Cuando evitamos usar try-except, se siguen generando excepciones. Las excepciones que no se manejan simplemente van al controlador predeterminado. Cuando utiliza try-except, puede manejar el error usted mismo. Podría ser más eficiente porque if-else requiere una evaluación de la condición, mientras que buscar un controlador de excepciones puede ser más barato. Incluso si esto es cierto, la ganancia será demasiado pequeña como para preocuparse por pensar.

Espero que mi respuesta ayude.

¿Qué son las llamadas y devoluciones estáticas frente a las dinámicas, y por qué crees que las llamadas y devoluciones son diferentes en Python dependiendo de si lo estás haciendo en un bloque de prueba / excepción? Incluso si no está detectando una excepción, Python aún tiene que manejar la llamada, posiblemente generando algo, por lo que no hace una diferencia en Python en cuanto a cómo se manejan las llamadas y las devoluciones.

Cada llamada a una función en Python involucra empujar los argumentos en la stack, e invocar el llamable. Cada terminación de una sola función es seguida por la persona que llama, en el cableado interno de Python, verificando una terminación exitosa o excepcional, y la maneja en consecuencia. En otras palabras, si piensa que hay un manejo adicional cuando está en un bloque try / except que de alguna manera se omite cuando no está en uno, está equivocado. Supongo que de eso se trata la distinción entre “estática” y “dinámica”.

Además, es una cuestión de estilo, y los desarrolladores experimentados de Python vienen a leer la excepción que capta bien, de modo que cuando ven el bash adecuado, excepto en torno a una llamada, es más legible que una verificación condicional.

El mensaje general, como dijo S.Lott, es que intentar / excepto no duele, por lo que debe sentirse libre de usarlo cuando le parezca apropiado.

Este debate a menudo se llama “LBYL vs EAFP”, que es “mirar antes de saltar” vs “más fácil pedir perdón que permiso”. Alex Martelli pesa sobre el tema aquí: http://mail.python.org/pipermail/python-list/2003-May/205182.html Este debate tiene casi seis años, pero no creo que los temas básicos tengan Cambió mucho.