2011-10-31 8 views
13

Al intentar eliminar la posible condición de carrera en un módulo de Python que escribí para monitorear algunos flujos de trabajo especializados, aprendí sobre el estilo de codificación de python "EA fácil de pedir perdón" (EAFP), y ahora estoy planteando muchas excepciones personalizadas try/except blocks donde solía usar if/thens.¿Con qué frecuencia se deben definir las excepciones personalizadas en python?

Soy nuevo en python y este estilo de EAFP tiene sentido lógicamente y parece hacer que mi código sea más robusto, pero algo sobre esto se siente muy por la borda. ¿Es una mala práctica definir una o más excepciones por método?

Estas excepciones personalizadas tienden a ser útiles solo para un único método y, si bien se siente como una solución funcionalmente correcta, parece que es necesario mantener un gran número de códigos.

Aquí un método de muestreo por ejemplo:

class UploadTimeoutFileMissing(Exception): 
    def __init__(self, value): 
     self.parameter = value 
    def __str__(self): 
     return repr(self.parameter) 

class UploadTimeoutTooSlow(Exception): 
    def __init__(self, value): 
     self.parameter = value 
    def __str__(self): 
     return repr(self.parameter) 

def check_upload(file, timeout_seconds, max_age_seconds, min_age_seconds): 

    timeout = time.time() + timeout_seconds 

    ## Check until file found or timeout 
    while (time.time() < timeout): 

     time.sleep(5) 
     try: 
      filetime = os.path.getmtime(file) 
      filesize = os.path.getsize(file) 
     except OSError: 
      print "File not found %s" % file 
      continue 

     fileage = time.time() - filetime 

     ## Make sure file isn't pre-existing 
     if fileage > max_age_seconds: 
      print "File too old %s" % file 
      continue 

     ## Make sure file isn't still uploading 
     elif fileage <= min_age_seconds: 
      print "File too new %s" % file 
      continue 

     return(filetime, filesize) 

    ## Timeout 
    try: 
     filetime 
     filesize 
     raise UploadTimeoutTooSlow("File still uploading") 

    except NameError: 
     raise UploadTimeoutFileMissing("File not sent") 
+1

La biblioteca estándar de Python tiene ~ 200k líneas de código y se arregla con 165 excepciones. ¿Seguro que necesita la suya? (Extraje los números de una charla llamada "[Dejar de escribir clases] (http://youtu.be/o9pEzgHorH0)") – Glider

Respuesta

5

definir una o más excepciones al método

Si se refiere a que la excepción es en realidad definida por método que en el "del cuerpo del método", entonces sí . Esa es una mala práctica. Esto también es cierto si define dos excepciones que se relacionan con el mismo error, pero crea dos porque los plantean dos métodos diferentes.

Si usted pregunta si es una mala práctica a aumento más de una excepción por método, entonces sin, es una buena práctica. Y si los errores no son de la misma categoría, está perfectamente bien definir varias excepciones por módulo.

En general, para los módulos más grandes definirá más de una excepción. Si trabajases en alguna biblioteca aritmética y definirías un ZeroDivisionError y un OverflowError (si no estaban ya definidos en python, porque puedes volver a usarlos, por supuesto) eso estaría perfectamente bien.

+0

, puede haber significado el número (recuento) de excepciones en relación con el recuento de métodos. es decir. 3 métodos => 4 excepciones, 8 métodos => 11 excepciones – n611x007

3

Es una mala práctica para definir una o más excepciones al método?

Sí.

Uno por módulo es más típico. Depende, por supuesto, de la semántica detallada. La pregunta se reduce a esto: "¿Qué intentas atrapar realmente?"

Si nunca va a utilizar except ThisVeryDetailedException: en su código, su muy detallada excepción no es muy útil.

Si puede hacer esto: except Error as e: if e.some_special_case por las pocas veces que importa, entonces puede simplificar fácilmente a una excepción por módulo y manejar sus casos especiales como atributos de la excepción en lugar de diferentes tipos de excepciones.

Las sugerencias comunes (una por módulo, llamado Error) significa que su código a menudo se verá así.

try: 
    something 
except some_module.Error as e: 
    carry on 

Esto le da una buena convención de nomenclatura: module.Error. Esto cubre numerosos pecados.


En una nota relacionada, si usted cree que tiene "condición de carrera potencial" probablemente debería rediseñar las cosas correctamente o dejar de tratar de utilizar hilos o cambiar a multiprocesamiento. Si usa multiprocesamiento, encontrará que es muy fácil evitar las condiciones de carrera.

0

No creo que sea necesario tener una excepción extremadamente específica para cada escenario posible. Un solo UploadTimeoutError probablemente estaría bien, y usted puede simplemente personalizar la cadena de excepción: para eso están las cadenas, después de todo. Tenga en cuenta que Python no tiene una excepción por separado para cada tipo posible de error de sintaxis, solo un SyntaxError general.

Además, ¿es realmente necesario definir los métodos __init__ y __str__ para cada una de sus excepciones personalizadas? Por lo que yo puedo decir, si usted no está de implementar cualquier comportamiento inusual, no es necesario añadir ningún código:

>>> class MyException(Exception): pass 
... 
>>> raise MyException("oops!") 
Traceback (most recent call last): 
    File "<ipython console>", line 1, in <module> 
MyException: oops!  
>>> str(MyException("oops!")) 
'oops!' 
3

voy a opinar sobre esto porque son excepciones personalizadas caro a mi corazón. Explicaré mis circunstancias y el lector puede compararlas con las suyas.

Soy el arquitecto de tuberías para una empresa de efectos visuales. La mayoría de lo que hago implica desarrollar lo que llamo la "API de instalaciones". Es un sistema de muchos módulos que manejan todo desde localizar cosas en el sistema de archivos. gestionar la configuración del módulo/herramienta/proyecto, manejar tipos de datos de varias aplicaciones CG para permitir la colaboración.

Me esfuerzo por asegurarme de que las excepciones integradas de Python nunca se disparen. Dado que nuestros desarrolladores dependerán de un ecosistema de módulos existentes para construir sus propias herramientas además, tener la API deja que un escape genérico IOError sea contraproducente, especialmente dado que la rutina de llamadas podría no ser consciente de que está leyendo el sistema de archivos (la abstracción es Algo hermoso). Si el módulo subyacente no puede expresar algo significativo sobre ese error, se necesita hacer más trabajo.

Mi enfoque para resolver esto es crear una clase de excepción de facilidad de la que se derivan todas las otras excepciones de instalación. Hay subclases de eso para tipos específicos de tareas o aplicaciones de host específicas, lo que me permite personalizar el manejo de errores (por ejemplo, las excepciones planteadas en Maya lanzarán una IU para ayudar a solucionar problemas ya que la excepción habitual se generaría en una consola discreta y a menudo se echaría de menos).

Todo tipo de informes está integrado en la clase de excepción de facilidad: las excepciones no aparecen para un usuario sin que también se denuncien internamente. Para una variedad de excepciones, obtengo un IM cada vez que se cría. Otros simplemente informan silenciosamente en una base de datos que puedo consultar para informes recientes (diarios o semanales). Cada uno enlaza con datos EXTENSOS capturados de la sesión del usuario, que generalmente incluyen una captura de pantalla, seguimiento de pila, configuración del sistema y mucho más. Esto significa que puedo solucionar los problemas de manera efectiva antes de que se informen, y tengo más información a mi alcance de la que la mayoría de los usuarios probablemente puedan proporcionar.

Se desaconsejan las gradaciones muy finas: las excepciones aceptan valores pasados ​​(a veces incluso un diccionario en lugar de una cadena, si queremos proporcionar una gran cantidad de datos para solucionar problemas) para proporcionar su salida formateada.

Así que no, no creo que la definición de una o dos excepciones por módulo no sea razonable, pero deben ser significativas y agregar algo al proyecto. Si solo está envolviendo un IOError al raise MyIOError("I got an IO error!"), entonces quizás quiera reconsiderar eso.

Cuestiones relacionadas