Ответ 1
Однако, если он не может подключиться, то
db
не будет существовать дальше - поэтому я установилdb = None
выше. Однако это хорошая практика?
Нет, настройка db = None
не самая лучшая практика. Есть две возможности: либо подключение к базе данных будет работать, либо не будет.
-
Подключение к базе данных не работает:
По мере того, как поднятое исключение было поймано и не было повторно поднято, вы продолжаете, пока не достигнете
cursor = db.Cursor()
.db == None
, поэтому будет создано исключение, напоминающееTypeError: 'NoneType' object has no attribute 'Cursor'
. Поскольку исключение, сгенерированное при сбое соединения с базой данных, уже было обнаружено, причина отказа маскируется.Лично я всегда вызываю исключение соединения, если вы не попробуете снова в ближайшее время. Как вы это поймете, зависит от вас; если ошибка сохраняется, я пишу по электронной почте, чтобы сказать "идите и проверьте базу данных".
-
Подключение к базе данных работает:
Переменная
db
назначается в вашем блокеtry:... except
. Если методconnect
работает, тоdb
заменяется объектом соединения.
В любом случае начальное значение db
никогда не используется.
Однако я слышал, что использование обработки исключений для управления потоком это плохая практика.
В отличие от других языков, Python использует обработку исключений для управления потоком. В конце моего ответа я связался с несколькими вопросами о переполнении стека и программистами, которые задают аналогичный вопрос. В каждом примере вы увидите слова "но на Python".
Это не означает, что вы должны переходить за борт, но Python обычно использует мантру EAFP: "Проще просить прощения, чем разрешения". В первую тройку проголосовали примеры в Как проверить, существует ли переменная? являются хорошими примерами того, как вы можете использовать управление потоком или нет.
Является ли исключение гнездования хорошей идеей? Или есть лучший способ с зависимыми/каскадными исключениями вроде этого?
Нет ничего плохого в вложенных исключениях, еще раз, пока вы делаете это здорово. Рассмотрите свой код. Вы можете удалить все исключения и обернуть всю вещь в блоке try:... except
. Если возникает исключение, вы знаете, что это было, но немного сложнее определить, что пошло не так.
Что произойдет, если вы захотите сказать по электронной почте о провале cursor.execute
? У вас должно быть исключение вокруг cursor.execute
, чтобы выполнить эту задачу. Затем вы повторно поднимаете исключение, чтобы оно попало в ваш внешний try:...
. Не повторное поднятие приведет к тому, что ваш код будет продолжаться, как будто ничего не произошло, и любая логика, которую вы внесли в ваш внешний try:...
для обработки исключения, будет проигнорирована.
В конечном счете все исключения наследуются от BaseException
.
Кроме того, есть некоторые части (например, сбои подключения), где мне бы хотелось script, чтобы просто закончить - отсюда вызывается вызов sys.exit().
Я добавил простой класс и как его назвать, что примерно так, как я буду делать то, что вы пытаетесь сделать. Если это будет запущено в фоновом режиме, то печать ошибок не стоит - люди не будут сидеть там, глядя вручную на ошибки. Они должны регистрироваться независимо от вашего стандартного способа и уведомлять соответствующих людей. По этой причине я удалил печать и заменил ее напоминанием о регистрации.
Поскольку я разбил класс на несколько функций, когда метод connect
завершился с ошибкой, и возникло исключение, вызов execute
не будет запущен, а script завершит работу после попытки отсоединения.
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()
Затем назовите его:
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()
Дальнейшее чтение:
Почему бы не использовать исключения в качестве регулярного потока управления?
Является ли обработка исключений python более эффективной, чем PHP и/или другие языки?
Аргументы за или против использования try catch в качестве логических операторов