validar try tipos print por excepción excepciones error ejemplos diferentes archivos anidados python exception

try - validar division por 0 en python



¿Manera correcta de manejar las excepciones en Python? (7)

He buscado otras publicaciones, ya que sentí que este es un problema bastante común, pero todas las demás preguntas de excepción de Python que encontré no reflejaban mi problema.

Intentaré ser lo más específico posible aquí, así que daré un ejemplo directo. Y pleeeeease no publique ninguna solución para este problema específico. No me interesa específicamente cómo puedes enviar un correo electrónico mucho mejor con xyz. Quiero saber cómo lidia con las declaraciones dependientes y propensas a errores.

Mi pregunta es, cómo manejar bien las excepciones, las que dependen unas de otras, lo que significa: Solo si el primer paso fue exitoso, intente el siguiente, y así sucesivamente. Un criterio más es: todas las excepciones deben ser detectadas, este código debe ser sólido.

Para su consideración, un ejemplo:

try: server = smtplib.SMTP(host) #can throw an exception except smtplib.socket.gaierror: #actually it can throw a lot more, this is just an example pass else: #only if no exception was thrown we may continue try: server.login(username, password) except SMTPAuthenticationError: pass # do some stuff here finally: #we can only run this when the first try...except was successful #else this throws an exception itself! server.quit() else: try: # this is already the 3rd nested try...except # for such a simple procedure! horrible server.sendmail(addr, [to], msg.as_string()) return True except Exception: return False finally: server.quit() return False

Para mí, esto me parece muy poco profético, y el código de manejo de errores triplica el código de la empresa real, pero, por otro lado, ¿cómo puedo manejar varias declaraciones que dependen unas de otras, lo que significa que declaración1 es un requisito previo para la declaración2 y así sucesivamente?

También estoy interesado en la limpieza adecuada de los recursos, incluso Python puede gestionar eso por sí mismo.

Gracias tom


¿Por qué no un gran intento: bloquear? De esta manera, si se detecta alguna excepción, irá hasta la excepción. Y siempre que todas las excepciones para los diferentes pasos sean diferentes, siempre puede decir qué parte fue la que disparó la excepción.


En general, desea utilizar la menor cantidad posible de bloques de prueba, distinguiendo las condiciones de falla por el tipo de excepciones que lanzan. Por ejemplo, aquí está mi refactorización del código que publicó:

try: server = smtplib.SMTP(host) server.login(username, password) # Only runs if the previous line didn''t throw server.sendmail(addr, [to], msg.as_string()) return True except smtplib.socket.gaierror: pass # Couldn''t contact the host except SMTPAuthenticationError: pass # Login failed except SomeSendMailError: pass # Couldn''t send mail finally: if server: server.quit() return False

Aquí, utilizamos el hecho de que smtplib.SMTP (), server.login () y server.sendmail () lanzan diferentes excepciones para aplanar el árbol de bloques try-catch. En el bloque finally, probamos el servidor explícitamente para evitar invocar a quit () en el objeto nulo.

También podríamos usar tres bloques secuenciales try-catch, devolviendo False en las condiciones de excepción, si hay casos de excepción superpuestos que deben manejarse por separado:

try: server = smtplib.SMTP(host) except smtplib.socket.gaierror: return False # Couldn''t contact the host try: server.login(username, password) except SMTPAuthenticationError: server.quit() return False # Login failed try: server.sendmail(addr, [to], msg.as_string()) except SomeSendMailError: server.quit() return False # Couldn''t send mail return True

Esto no es tan bueno, ya que tiene que matar el servidor en más de un lugar, pero ahora podemos manejar tipos de excepción específicos de diferentes maneras en diferentes lugares sin mantener ningún estado adicional.


En lugar de usar el bloque try / except''s else, simplemente podría regresar cuando se produzca un error:

def send_message(addr, to, msg): ## Connect to host try: server = smtplib.SMTP(host) #can throw an exception except smtplib.socket.gaierror: return False ## Login try: server.login(username, password) except SMTPAuthenticationError: server.quit() return False ## Send message try: server.sendmail(addr, [to], msg.as_string()) return True except Exception: # try to avoid catching Exception unless you have too return False finally: server.quit()

Eso es perfectamente legible y Pythonic ..

Otra forma de hacer esto es, en lugar de preocuparse por la implementación específica, decidir cómo desea que se vea su código, por ejemplo ...

sender = MyMailer("username", "password") # the except SocketError/AuthError could go here try: sender.message("addr..", ["to.."], "message...") except SocketError: print "Couldn''t connect to server" except AuthError: print "Invalid username and/or password!" else: print "Message sent!"

Luego, escriba el código para el método message() , detecte los errores que espera y aumente su propio error personalizado, y manéjelo donde sea relevante. Tu clase puede parecer algo como ...

class ConnectionError(Exception): pass class AuthError(Exception): pass class SendError(Exception): pass class MyMailer: def __init__(self, host, username, password): self.host = host self.username = username self.password = password def connect(self): try: self.server = smtp.SMTP(self.host) except smtplib.socket.gaierror: raise ConnectionError("Error connecting to %s" % (self.host)) def auth(self): try: self.server.login(self.username, self.password) except SMTPAuthenticationError: raise AuthError("Invalid username (%s) and/or password" % (self.username)) def message(self, addr, to, msg): try: server.sendmail(addr, [to], msg.as_string()) except smtplib.something.senderror, errormsg: raise SendError("Couldn''t send message: %s" % (errormsg)) except smtp.socket.timeout: raise ConnectionError("Socket error while sending message")


Me gusta la respuesta de David, pero si está atascado en las excepciones del servidor, también puede verificar si el servidor es Ninguno o estados. Aplasté el método un poco, pero sigue siendo un aspecto poco antipónico pero más legible en la lógica de abajo.

server = None def server_obtained(host): try: server = smtplib.SMTP(host) #can throw an exception return True except smtplib.socket.gaierror: #actually it can throw a lot more, this is just an example return False def server_login(username, password): loggedin = False try: server.login(username, password) loggedin = True except SMTPAuthenticationError: pass # do some stuff here finally: #we can only run this when the first try...except was successful #else this throws an exception itself! if(server is not None): server.quit() return loggedin def send_mail(addr, to, msg): sent = False try: server.sendmail(addr, to, msg) sent = True except Exception: return False finally: server.quit() return sent def do_msg_send(): if(server_obtained(host)): if(server_login(username, password)): if(send_mail(addr, [to], msg.as_string())): return True return False


Probaría algo como esto:

class Mailer(): def send_message(self): exception = None for method in [self.connect, self.authenticate, self.send, self.quit]: try: if not method(): break except Exception, ex: exception = ex break if method == quit and exception == None: return True if exception: self.handle_exception(method, exception) else: self.handle_failure(method) def connect(self): return True def authenticate(self): return True def send(self): return True def quit(self): return True def handle_exception(self, method, exception): print "{name} ({msg}) in {method}.".format( name=exception.__class__.__name__, msg=exception, method=method.__name__) def handle_failure(self, method): print "Failure in {0}.".format(method.__name__)

Todos los métodos (incluido send_message , en realidad) siguen el mismo protocolo: devuelven True si tuvieron éxito y, a menos que manejen una excepción, no la atrapan. Este protocolo también hace posible manejar el caso en el que un método debe indicar que falló sin generar una excepción. (Si la única forma en que sus métodos fallan es al generar una excepción, eso simplifica el protocolo. Si tiene que lidiar con una gran cantidad de estados de fallas no excepcionales fuera del método que falló, es probable que tenga un problema de diseño no he trabajado todavía.)

La desventaja de este enfoque es que todos los métodos tienen que usar los mismos argumentos. No he optado por ninguno, con la expectativa de que los métodos que eliminé terminarán manipulando a los miembros de la clase.

Sin embargo, la ventaja de este enfoque es considerable. Primero, puede agregar docenas de métodos al proceso sin que send_message se send_message más complejo.

También puedes volverte loco y hacer algo como esto:

def handle_exception(self, method, exception): custom_handler_name = "handle_{0}_in_{1}".format(/ exception.__class__.__name__, method.__name__) try: custom_handler = self.__dict__[custom_handler_name] except KeyError: print "{name} ({msg}) in {method}.".format( name=exception.__class__.__name__, msg=exception, method=method.__name__) return custom_handler() def handle_AuthenticationError_in_authenticate(self): print "Your login credentials are questionable."

... aunque en ese punto, podría decirme a mí mismo, "yo, estás trabajando el patrón de Comando bastante duro sin crear una clase de Comando. Tal vez ahora sea el momento".


Si fuera yo, probablemente haría algo como lo siguiente:

try: server = smtplib.SMTP(host) try: server.login(username, password) server.sendmail(addr, [to], str(msg)) finally: server.quit() except: debug("sendmail", traceback.format_exc().splitlines()[-1]) return True

Todos los errores son detectados y depurados, el valor de retorno == Verdadero en caso de éxito, y la conexión del servidor se limpia correctamente si se realiza la conexión inicial.


Solo usar un bloque de prueba es el camino a seguir. Esto es exactamente para lo que están diseñados: solo ejecute la siguiente instrucción si la declaración anterior no produjo una excepción. En cuanto a las limpiezas de recursos, quizás pueda verificar el recurso si necesita ser limpiado (por ejemplo, myfile.is_open (), ...) Esto agrega algunas condiciones adicionales, pero solo se ejecutarán en el caso excepcional . Para manejar el caso de que la misma Excepción pueda ser provocada por diferentes razones, debería poder recuperar la razón de la Excepción.

Sugiero código como este:

server = None try: server = smtplib.SMTP(host) #can throw an exception server.login(username, password) server.sendmail(addr, [to], msg.as_string()) server.quit() return True except smtplib.socket.gaierror: pass # do some stuff here except SMTPAuthenticationError: pass # do some stuff here except Exception, msg: # Exception can have several reasons if msg==''xxx'': pass # do some stuff here elif: pass # do some other stuff here if server: server.quit() return False

No es raro que el código de manejo de errores supere el código de negocio. El correcto manejo de errores puede ser complejo. Pero para aumentar la capacidad de mantenimiento, ayuda a separar el código de negocio del código de manejo de errores.