Men om den inte kan ansluta,
db
kommer inte att existera längre ner - det är därför jag ställer indb = None
ovan. Men är det en bra praxis?
Nej, inställningen db = None
är inte bästa praxis. Det finns två möjligheter, antingen fungerar det att ansluta till databasen eller inte.
-
Att ansluta till databasen fungerar inte:
Eftersom det höjda undantaget har fångats och inte höjts igen fortsätter du tills du når
cursor = db.Cursor()
.db == None
, alltså ett undantag som liknarTypeError: 'NoneType' object has no attribute 'Cursor'
kommer att höjas. Eftersom undantaget som genererades när databasanslutningen misslyckades redan har fångats, är orsaken till felet förtäckt.Personligen skulle jag alltid ta upp ett anslutningsundantag om du inte ska försöka igen inom kort. Hur du fångar det är upp till dig; om felet kvarstår skickar jag ett e-postmeddelande och säger "gå och kolla databasen".
-
Att ansluta till databasen fungerar:
Variabeln
db
är tilldelad i ditttry:... except
blockera. Omconnect
Metoden fungerar dådb
ersätts med anslutningsobjektet.
Hur som helst startvärdet för db
används aldrig.
Jag har dock hört att det är dålig praxis att använda undantagshantering för flödeskontroll som detta.
Till skillnad från andra språk gör Python använd undantagshantering för flödeskontroll. I slutet av mitt svar har jag länkat till flera frågor om Stack Overflow och programmerare som ställer en liknande fråga. I varje exempel ser du orden "men i Python".
Därmed inte sagt att du ska gå överbord men Python använder i allmänhet mantrat EAFP, "Det är lättare att be om förlåtelse än om tillåtelse." De tre bästa exemplen i Hur kontrollerar jag om en variabel finns? är bra exempel på hur man både kan använda flödeskontroll eller inte.
Är häckande undantag en bra idé? Eller finns det ett bättre sätt att hantera beroende/kaskadkopplade undantag som detta?
Det är inget fel med kapslade undantag, än en gång så länge du gör det förnuftigt. Tänk på din kod. Du kan ta bort alla undantag och slå in det hela i ett try:... except
blockera. Om ett undantag tas upp så vet du vad det var, men det är lite svårare att spåra exakt vad som gick fel.
Vad händer sedan om du själv vill säga e-post om felet i cursor.execute
? Du bör ha ett undantag kring cursor.execute
för att utföra denna enda uppgift. Du höjer sedan undantaget igen så att det fångas i ditt yttre try:...
. Att inte höja igen skulle resultera i att din kod fortsätter som om ingenting hade hänt och vilken logik du än hade lagt i ditt yttre try:...
att hantera ett undantag skulle ignoreras.
I slutändan ärvs alla undantag från BaseException
.
Det finns också vissa delar (t.ex. anslutningsfel) där jag vill att skriptet bara ska avslutas - därav det kommenterade sys.exit()-anropet.
Jag har lagt till en enkel klass och hur man kallar den, vilket är ungefär hur jag skulle göra det du försöker göra. Om detta ska köras i bakgrunden är det inte lönt att skriva ut felen - folk kommer inte att sitta där manuellt och leta efter fel. De bör vara inloggade på vilket standardsätt du än är och lämpliga personer meddelas. Jag har tagit bort utskriften av denna anledning och ersatt med en påminnelse om att logga.
Eftersom jag har delat upp klassen i flera funktioner när connect
metoden misslyckas och ett undantag höjs till execute
samtalet kommer inte att köras och skriptet kommer att slutföras efter ett försök att koppla från.
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()
Kalla det sedan:
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()
Mer läsning:
cx_Oracle
dokumentation
Varför inte använda undantag som regelbundet kontrollflöde?
Är python-undantagshantering effektivare än PHP och/eller andra språk?
Argument för eller emot att använda try catch som logiska operatorer