No entanto, se não puder se conectar,db
não existirá mais abaixo - e é por isso que definodb = None
acima de. No entanto, isso é uma boa prática?
Não, configurando
db = None
não é a melhor prática. Existem duas possibilidades, conectar-se ao banco de dados funcionará ou não. -
A conexão com o banco de dados não funciona:
Como a exceção levantada foi capturada e não re-aumentada, você continua até chegar acursor = db.Cursor()
.
db == None
, portanto, uma exceção que se assemelha aTypeError: 'NoneType' object has no attribute 'Cursor'
será levantada. Como a exceção gerada quando a conexão com o banco de dados falhou já foi capturada, o motivo da falha é disfarçado.
Pessoalmente, eu sempre levantaria uma exceção de conexão, a menos que você tente novamente em breve. Como você vai pegá-lo depende de você; se o erro persistir, mando um e-mail para dizer "vá e verifique o banco de dados".
-
A conexão com o banco de dados funciona:
A variáveldb
é atribuído em seutry:... except
quadra. Se oconnect
método funciona entãodb
é substituído pelo objeto de conexão.
De qualquer forma, o valor inicial de
db
nunca é usado.
No entanto, ouvi dizer que usar tratamento de exceção para controle de fluxo como isso é uma prática ruim.
Ao contrário de outras linguagens, o Python faz use tratamento de exceção para controle de fluxo. No final da minha resposta, vinculei várias perguntas sobre o Stack Overflow e os programadores que fazem uma pergunta semelhante. Em todos os exemplos, você verá as palavras "mas em Python".
Isso não quer dizer que você deva exagerar, mas o Python geralmente usa o mantra EAFP, "É mais fácil pedir perdão do que permissão." Os três principais exemplos votados em Como verifico se uma variável existe? são bons exemplos de como você pode usar o controle de fluxo ou não.
Aninhar exceções é uma boa ideia? Ou existe uma maneira melhor de lidar com exceções dependentes/em cascata como essa?
Não há nada de errado com exceções aninhadas, mais uma vez, desde que você faça isso de forma sensata. Considere o seu código. Você pode remover todas as exceções e envolver tudo em um
try:... except
quadra. Se uma exceção for levantada, você saberá o que foi, mas é um pouco mais difícil rastrear exatamente o que deu errado. O que acontece se você quiser enviar um e-mail sobre a falha do
cursor.execute
? Você deve ter uma exceção em torno de cursor.execute
para realizar esta tarefa. Você então re-aumenta a exceção para que ela seja capturada em seu try:...
externo . Não re-aumentar resultaria em seu código continuando como se nada tivesse acontecido e qualquer lógica que você tivesse colocado em seu try:...
externo lidar com uma exceção seria ignorado. Em última análise, todas as exceções são herdadas de
BaseException
.
Além disso, existem algumas partes (por exemplo, falhas de conexão) em que eu gostaria que o script terminasse - daí a chamada sys.exit() comentada.
Eu adicionei uma classe simples e como chamá-la, que é mais ou menos como eu faria o que você está tentando fazer. Se isso for executado em segundo plano, a impressão dos erros não vale a pena - as pessoas não ficarão sentadas manualmente procurando erros. Eles devem ser registrados em qualquer que seja sua maneira padrão e as pessoas apropriadas notificadas. Eu removi a impressão por esse motivo e a substituí por um lembrete para registrar.
Como eu dividi a classe em várias funções quando o
connect
O método falha e uma exceção é gerada a execute
chamada não será executada e o script será concluído, após tentar desconectar. 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()
Então chame-o:
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()
Leitura adicional:
cx_Oracle
documentação Por que não usar exceções como fluxo regular de controle?
O tratamento de exceções python é mais eficiente que PHP e/ou outras linguagens?
Argumentos a favor ou contra o uso de try catch como operadores lógicos