2011-09-06 8 views
7

Tengo una serie de funciones que analizan los datos de los archivos, generalmente devuelven una lista de resultados.Quiero devolver un valor Y presentar una excepción, ¿significa que estoy haciendo algo mal?

Si encuentro una línea dudosa en el archivo, quiero usar Soldier y procesar las líneas válidas, y devolverlas. Pero también quiero informar el error a la función de llamada. La razón por la que deseo informarlo es para que la función de llamada pueda notificar al usuario que el archivo debe ser revisado. No quiero comenzar a hacer cosas de GUI en la función de análisis, ya que parece ser una gran violación de la separación de las preocupaciones. La función de análisis no tiene acceso a la consola a la que estoy escribiendo mensajes de error.

Esto me deja con ganas de devolver los datos exitosos, pero también genera una excepción debido al error, lo que claramente no puedo hacer.

consideran este código:

try: 
    parseResult = parse(myFile) 
except MyErrorClass, e: 
    HandleErrorsSomehow(str(e)) 

def parse(file): #file is a list of lines from an actual file 
    err = False 
    result = [] 

    for lines in file: 
     processedLine = Process(line) 
     if not processedLine: 
      err = True 
     else 
      result.append(processedLine) 
    return result 
    if err: 
     raise MyErrorClass("Something went wrong") 

Obviamente las tres últimas líneas no tienen sentido, pero no puedo encontrar una buena manera de hacer esto. Creo que podría hacer return (err, result), y lo llaman como

parseErr, parseResult = parse(file) 
if parseErr: 
    HandleErrorsSomehow() 

Pero volviendo códigos de error parece bastante ONU-Pythonic, y mucho menos tuplas de códigos de error y los valores de los resultados reales de regresar.

El hecho de que siento que quiero hacer algo tan extraño en una aplicación que no debería ser terriblemente complicada, me hace pensar que probablemente estoy haciendo algo mal. ¿Hay una mejor solución para este problema? ¿O hay alguna forma de que pueda usar finally para devolver un valor y lanzar una excepción al mismo tiempo?

+0

En este caso, probablemente devolvería una tupla 'processedData, errorInformation', con' errorInformation' siendo una cadena vacía si no se produjo ningún error. Otra opción sería utilizar una función de devolución de llamada, llamada con información de error si ocurre un error: si a la persona que llama no le importa, podría pasar 'None' como devolución de llamada. –

Respuesta

7

Nadie dice solo manera válida de tratar un "error" es lanzar una excepción.

En su diseño, la persona que llama desea dos datos: (1) los datos válidos, (2) si ocurrió un error (y probablemente algo sobre qué fue mal, para que pueda formatear un mensaje de error útil). Es un caso completamente válido y por encima de la tierra para devolver un par de valores.

Un diseño alternativo sería pasar una colección mutable hacia abajo a la función como parámetro y dejar que complete los mensajes de error que desea emitir en eso. A menudo, esto simplificará la instalación de plomería en la persona que llama, especialmente si hay varias capas de llamadas entre el analizador y el código que sabe cómo hacer algo con los mensajes de error después.

+0

Su última oración: esa fue una de las razones principales por las que busqué una mejor manera de hacerlo. No había pensado en usar un 'parámetro de salida'. (¿Tienen un mejor nombre?) –

+0

Si bien las advertencias emitidas pueden haber sido más elegantes en un mundo ideal, el hecho de que Jython solo tenga hasta 2.5 lo hizo poco práctico para mí. Terminé pasando una lista para completar con la función de análisis. Esto funciona especialmente bien en lugares donde llamo a algunas funciones de análisis diferentes en una fila, y luego puedo iterar sobre toda la lista de advertencias una vez que están listas. Me gusta mucho más que devolver tuplas y consultar valores de devolución por separado. –

+1

@CamJackson para situaciones como esta. Con frecuencia he encontrado que pasar una devolución de llamada con una firma de llamada específica funciona mejor que pasar una lista sin formato. De esta manera, es fácil abstraer la implementación de las partes internas, así como poner una devolución de llamada cuando a la persona que llama no le preocupe recuperar los errores. Con frecuencia, puede ser tan simple como pasar el método '.append' encuadernado de la lista que desea agregar. Hace que sea más claro que algo se está transmitiendo (los parámetros de "salida" no siempre son obvios), y permite que la persona que llama realice un procesamiento complejo en el momento del error. –

4

Emit a warning en su lugar, y deje que el código decida cómo manejarlo.

+0

eso es lindo. no tenía idea de que el módulo existía. parece que las advertencias se pueden conectar al registro de Python, que a su vez podría estar conectado a la GUI. –

+0

Esto parece prometedor, investigando esto ahora. Lo único es que estoy usando Jython, que no parece tener 'warnings.catch_warnings()', así que no estoy seguro de cómo debo manejar las advertencias. ¿Puedo tratar de atraparlos como una excepción? –

+0

Hmmm, leí [este] (http://www.jython.org/jythonbook/en/1.0/ExceptionHandlingDebug.html#issuing-warnings) pero no dice nada sobre cómo la función de llamada puede capturar las advertencias . A menos que use un filtro para convertir advertencias en excepciones, pero luego estoy de vuelta donde comencé ¿no? ¿Sabes cómo manejar las advertencias sin usar 'catch_warnings()'? –

0

Vengo del mundo .Net, así que no sé cómo se traduce esto en Python ...

En casos como el suyo (en la que desea procesar numerosos artículos en una sola llamada) Me gustaría volver una MyProcessingResults objeto que celebró dos colecciones, por ejemplo:

  • MyProcessingResults.ProcessedLines - contiene todos los datos válidos que analizarse.
  • MyProcessingResults.Errors - contiene todos los errores (en el supuesto de que tiene más de uno y quiere saber explícitamente sobre todos ellos).
+2

Suena así sería simplemente devolver una tupla de los resultados y cualquier error. En python, una tupla puede contener objetos de diferentes tipos, por lo que no es necesario definir una clase que contenga los 2 objetos que quiero devolver si así lo quiero hacer. –

1

Otro posible diseño es invertir el control, pasando el controlador de errores como un parámetro.

(Además, no se siente como que tiene que contar Python cómo acumular datos en una lista. Ya sabe. No es difícil de hacer un trabajo de comprensión de lista aquí.)

def sample_handler(): 
    print "OMG, I wasn't expecting that; oh well." 

parseResult = parse(myFile, sample_handler) 

def parse(file, handler): #file is a list of lines from an actual file 
    result = [Process(line) for line in data] 
    if not all(result): handler() # i.e. if there are any false-ish values 
    result = filter(None, result) # remove false-ish values if any 
    return result 
+0

El formato de mis archivos de datos es un poco más complicado de lo que permití, así que de hecho tengo que iterar sobre la lista manualmente, pero ese es un método genial. ¡Python está lleno de agradables sorpresas! –

1

Dependiendo diseño de llamador el uso de devoluciones de llamada puede ser razonable:

def got_line(line): 
    print 'Got valid line', line 

def got_error(error): 
    print 'got error', error 

def parse(file, line, error): 
    for lines in file: 
     processedLine = Process(line) 
     if not processedLine: 
      error(MyErrorClass("Something went wrong")) 
     else 
      line(processedLine) 


parse(some_file, got_line, got_error) 
1

Me gustaría proponer una solución alternativa; usando una clase.

class MyParser(object): 
    def __init__(self): 
     self.warnings = [] 

    def parse(self, file): 
     ... 

Ahora la función de análisis puede establecer advertencias a la lista warnings, y el usuario puede comprobar esta lista si así lo desean.

Tan pronto como mis funciones comienzan a ser más avanzadas que simplemente "procesar esto y devolver mi valor", me gusta considerar usar una clase en su lugar. Hace un gran agrupamiento de código relacionado en un objeto y, a menudo, hace que el código sea más limpio y el uso más simple que las funciones que devuelven tuplas de información.

Cuestiones relacionadas