Sin embargo, si no se puede conectar, entonces db
no existirá más abajo, razón por la cual configuré db = None
sobre. Sin embargo, ¿es esa una buena práctica?
No, configurando db = None
no es la mejor práctica. Hay dos posibilidades, la conexión a la base de datos funcionará o no.
-
La conexión a la base de datos no funciona:
Como la excepción generada se detectó y no se volvió a generar, continúe hasta llegar a
cursor = db.Cursor()
.db == None
, entonces, una excepción que se parece aTypeError: 'NoneType' object has no attribute 'Cursor'
será elevado. Como la excepción generada cuando falló la conexión a la base de datos ya se detectó, el motivo del fallo está oculto.Personalmente, siempre generaría una excepción de conexión a menos que vuelva a intentarlo en breve. Cómo lo atrapes depende de ti; si el error persiste, envío un correo electrónico para decir "ir y verificar la base de datos".
-
La conexión a la base de datos funciona:
La variable
db
está asignado en sutry:... except
cuadra. Si elconnect
el método funciona entoncesdb
se reemplaza con el objeto de conexión.
De cualquier manera, el valor inicial de db
nunca se usa.
Sin embargo, he oído que usar el manejo de excepciones para el control de flujo como este es una mala práctica.
A diferencia de otros lenguajes, Python lo hace utilice el manejo de excepciones para el control de flujo. Al final de mi respuesta, he vinculado a varias preguntas sobre Desbordamiento de pila y Programadores que hacen una pregunta similar. En cada ejemplo, verá las palabras "pero en Python".
Eso no quiere decir que deba exagerar, pero Python generalmente usa el mantra EAFP, "Es más fácil pedir perdón que permiso". Los tres ejemplos más votados en ¿Cómo verifico si existe una variable? son buenos ejemplos de cómo puede usar el control de flujo o no.
¿Es una buena idea anidar excepciones? ¿O hay una mejor manera de lidiar con excepciones dependientes/en cascada como esta?
No hay nada de malo con las excepciones anidadas, una vez más, siempre y cuando lo hagas con cordura. Considere su código. Puede eliminar todas las excepciones y envolver todo en un try:... except
cuadra. Si se genera una excepción, sabrá cuál fue, pero es un poco más difícil rastrear exactamente qué salió mal.
Entonces, ¿qué sucede si desea enviar un correo electrónico a usted mismo sobre la falla de cursor.execute
? ? Debería tener una excepción alrededor de cursor.execute
para realizar esta única tarea. Luego, vuelve a generar la excepción para que quede atrapada en su try:...
. No volver a subir daría como resultado que su código continuara como si nada hubiera pasado y cualquier lógica que haya puesto en su try:...
tratar con una excepción sería ignorado.
En última instancia, todas las excepciones se heredan de BaseException
.
Además, hay algunas partes (por ejemplo, fallas de conexión) en las que me gustaría que la secuencia de comandos terminara, de ahí la llamada sys.exit() comentada.
Agregué una clase simple y cómo llamarla, que es más o menos cómo haría lo que está tratando de hacer. Si esto se va a ejecutar en segundo plano, la impresión de los errores no vale la pena:las personas no estarán sentadas allí buscando errores manualmente. Deben iniciar sesión sea cual sea su forma estándar y notificar a las personas adecuadas. Quité la impresión por este motivo y la reemplacé con un recordatorio para iniciar sesión.
Como he dividido la clase en múltiples funciones cuando connect
el método falla y se genera una excepción execute
la llamada no se ejecutará y el script finalizará después de intentar desconectarse.
import cx_Oracle
class Oracle(object):
def connect(self, username, password, hostname, port, servicename):
""" Connect to the database. """
try:
self.db = cx_Oracle.connect(username, password
, hostname + ':' + port + '/' + servicename)
except cx_Oracle.DatabaseError as e:
# Log error as appropriate
raise
# If the database connection succeeded create the cursor
# we-re going to use.
self.cursor = self.db.cursor()
def disconnect(self):
"""
Disconnect from the database. If this fails, for instance
if the connection instance doesn't exist, ignore the exception.
"""
try:
self.cursor.close()
self.db.close()
except cx_Oracle.DatabaseError:
pass
def execute(self, sql, bindvars=None, commit=False):
"""
Execute whatever SQL statements are passed to the method;
commit if specified. Do not specify fetchall() in here as
the SQL statement may not be a select.
bindvars is a dictionary of variables you pass to execute.
"""
try:
self.cursor.execute(sql, bindvars)
except cx_Oracle.DatabaseError as e:
# Log error as appropriate
raise
# Only commit if it-s necessary.
if commit:
self.db.commit()
Entonces llámalo:
if __name__ == "__main__":
oracle = Oracle.connect('username', 'password', 'hostname'
, 'port', 'servicename')
try:
# No commit as you don-t need to commit DDL.
oracle.execute('ddl_statements')
# Ensure that we always disconnect from the database to avoid
# ORA-00018: Maximum number of sessions exceeded.
finally:
oracle.disconnect()
Lectura adicional:
cx_Oracle
documentación
¿Por qué no usar excepciones como flujo regular de control?
¿Es Python el manejo de excepciones más eficiente que PHP y/u otros lenguajes?
Argumentos a favor o en contra del uso de try catch como operadores lógicos