Ако обаче не може да се свърже, тогава
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
.
Освен това има някои части (напр. неуспехи на връзката), където бих искал скриптът просто да приключи - оттук и коментираното извикване sys.exit().
Добавих прост клас и как да го нарека, което е приблизително как бих направил това, което се опитвате да направите. Ако това ще се изпълнява във фонов режим, тогава отпечатването на грешките не си струва - хората няма да седят там ръчно и да търсят грешки. Те трябва да бъдат влезли по какъвто и да е стандартният ви начин и да бъдат уведомени съответните хора. Премахнах отпечатването поради тази причина и го замених с напомняне за влизане.
Тъй като разделих класа на множество функции, когато connect
методът е неуспешен и се създава изключение execute
повикването няма да се изпълни и скриптът ще завърши, след като се опита да прекъсне връзката.
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()
Допълнително четене:
cx_Oracle
документация
Защо да не използвате изключения като редовен поток на контрол?
По-ефективно ли е обработката на изключенията в python от PHP и/или други езици?
Аргументи за или против използването на try catch като логически оператори