Jakie jest zamierzone zastosowanie opcjonalnej else
klauzuli try
oświadczenia?
Jakie jest zamierzone zastosowanie opcjonalnej else
klauzuli try
oświadczenia?
Odpowiedzi:
Instrukcje w else
bloku są wykonywane, jeśli wykonanie nie powiedzie się try
- jeśli nie było wyjątku. Szczerze mówiąc, nigdy nie znalazłem potrzeby.
Jednak uwagi na temat obsługi wyjątków :
Użycie klauzuli else jest lepsze niż dodanie dodatkowego kodu do klauzuli try, ponieważ pozwala uniknąć przypadkowego wyłapania wyjątku, który nie został zgłoszony przez kod chroniony przez instrukcję try ....
Tak więc, jeśli masz metodę, która może na przykład rzucić an IOError
, i chcesz wychwycić wyjątki, które ona wywołuje, ale jest coś innego, co chcesz zrobić, jeśli pierwsza operacja się powiedzie, i nie chcesz wychwycić błędu IOError z podczas tej operacji możesz napisać coś takiego:
try:
operation_that_can_throw_ioerror()
except IOError:
handle_the_exception_somehow()
else:
# we don't want to catch the IOError if it's raised
another_operation_that_can_throw_ioerror()
finally:
something_we_always_need_to_do()
Jeśli po prostu wstawisz another_operation_that_can_throw_ioerror()
później operation_that_can_throw_ioerror
, except
złapie błędy drugiego połączenia. A jeśli umieścisz go po całym try
bloku, zawsze będzie uruchamiany, i dopiero po finally
. else
Pozwala upewnić
finally
blokiem iIOError
podniesione przez niego przedmioty nie są tutaj łapanereturn
, continue
lub break
.
Jest jeden duży powód do użycia else
- styl i czytelność. Zasadniczo dobrym pomysłem jest przechowywanie kodu, który może powodować wyjątki w pobliżu kodu, który się nimi zajmuje. Na przykład porównaj te:
try:
from EasyDialogs import AskPassword
# 20 other lines
getpass = AskPassword
except ImportError:
getpass = default_getpass
i
try:
from EasyDialogs import AskPassword
except ImportError:
getpass = default_getpass
else:
# 20 other lines
getpass = AskPassword
Drugi jest dobry, gdy except
nie można wrócić wcześniej lub ponownie rzucić wyjątek. Jeśli to możliwe, napisałbym:
try:
from EasyDialogs import AskPassword
except ImportError:
getpass = default_getpass
return False # or throw Exception('something more descriptive')
# 20 other lines
getpass = AskPassword
Uwaga: Odpowiedź skopiowana z ostatnio opublikowanego duplikatu tutaj , stąd wszystkie te rzeczy „AskPassword”.
Jedno zastosowanie: przetestuj kod, który powinien zgłosić wyjątek.
try:
this_should_raise_TypeError()
except TypeError:
pass
except:
assert False, "Raised the wrong exception type"
else:
assert False, "Didn't raise any exception"
(Ten kod należy streścić w bardziej ogólny test w praktyce).
Python try-else
Jakie jest zamierzone użycie opcjonalnej
else
klauzuli instrukcji try?
Zamierzonym zastosowaniem jest kontekst, w którym można uruchomić więcej kodu, jeśli nie ma wyjątków, w których oczekiwano, że będzie on obsługiwany.
Ten kontekst pozwala uniknąć przypadkowej obsługi błędów, których się nie spodziewałeś.
Ale ważne jest, aby zrozumieć, o dokładnych warunkach, które powodują jeszcze klauzuli do biegu, ponieważ return
, continue
i break
może przerwać przepływ sterowania do else
.
else
Oświadczenie biegnie jeśli istnieją żadne wyjątki, a jeśli nie przerywa return
, continue
lub break
oświadczenia.
Opcjonalna
else
klauzula jest wykonywana wtedy, gdy kontrola spływa do końca tegotry
punktu. *
(Dodano pogrubienie.) A przypis brzmi:
* Obecnie kontrola „spływa końca” z wyjątkiem przypadku wyjątku lub wykonania
return
,continue
lubbreak
oświadczenia.
Wymaga co najmniej jednego poprzedzającego zdania z wyjątkiem klauzuli ( patrz gramatyka ). Więc tak naprawdę to nie jest „try-else”, to „try-else-else (-finally)” z else
(ifinally
) są opcjonalne.
W Python Tutorial omawia jego wykorzystania:
Instrukcja try ... else ma opcjonalną klauzulę else, która, jeśli jest obecna, musi być zgodna ze wszystkimi klauzulami oprócz. Jest to przydatne w przypadku kodu, który należy wykonać, jeśli klauzula try nie zgłosi wyjątku. Na przykład:
for arg in sys.argv[1:]: try: f = open(arg, 'r') except IOError: print 'cannot open', arg else: print arg, 'has', len(f.readlines()), 'lines' f.close()
Użycie klauzuli else jest lepsze niż dodanie dodatkowego kodu do klauzuli try, ponieważ pozwala uniknąć przypadkowego wyłapania wyjątku, który nie został zgłoszony przez kod chroniony przez instrukcję try ....
else
kodu w zależności odtry
blokuJeśli obsłużysz błąd, else
blok nie będzie działał. Na przykład:
def handle_error():
try:
raise RuntimeError('oops!')
except RuntimeError as error:
print('handled a RuntimeError, no big deal.')
else:
print('if this prints, we had no error!') # won't print!
print('And now we have left the try block!') # will print!
I teraz,
>>> handle_error()
handled a RuntimeError, no big deal.
And now we have left the try block!
Try-else-else jest świetny do łączenia wzoru EAFP z pisaniem kaczek :
try:
cs = x.cleanupSet
except AttributeError:
pass
else:
for v in cs:
v.cleanup()
Być może ten naiwny kod jest w porządku:
try:
for v in x.cleanupSet:
v.clenaup()
except AttributeError:
pass
To świetny sposób na przypadkowe ukrycie poważnych błędów w kodzie. Napisałem tam porządek, ale błąd AttributeError, który dałby mi znać, został połknięty. Co gorsza, co jeśli napisałbym go poprawnie, ale metoda czyszczenia była czasami przekazywana typowi użytkownika, który miał błędnie nazwany atrybut, powodując, że po cichu zawodzi w połowie i pozostawia plik niezamknięty? Powodzenia w debugowaniu tego.
Uważam, że jest to bardzo przydatne, gdy masz porządek, aby to zrobić, nawet jeśli istnieje wyjątek:
try:
data = something_that_can_go_wrong()
except Exception as e: # yes, I know that's a bad way to do it...
handle_exception(e)
else:
do_stuff(data)
finally:
clean_up()
Chociaż nie możesz teraz wymyślić zastosowania, możesz się założyć, że musi to być użyteczne. Oto niewyobrażalna próbka:
Z else
:
a = [1,2,3]
try:
something = a[2]
except:
print "out of bounds"
else:
print something
Bez else
:
try:
something = a[2]
except:
print "out of bounds"
if "something" in locals():
print something
Tutaj masz something
zdefiniowaną zmienną, jeśli nie zostanie zgłoszony błąd. Możesz usunąć to poza try
blokiem, ale wymaga to pewnego niechlujnego wykrycia, jeśli zmienna jest zdefiniowana.
something = a[2]; print something
środku try: block?
Jest ładny przykład try-else
w PEP 380 . Zasadniczo sprowadza się to do obsługi różnych wyjątków w różnych częściach algorytmu.
To jest coś takiego:
try:
do_init_stuff()
except:
handle_init_suff_execption()
else:
try:
do_middle_stuff()
except:
handle_middle_stuff_exception()
Umożliwia to zapisanie kodu obsługi wyjątku bliżej miejsca wystąpienia wyjątku.
Od błędów i wyjątków # Obsługa wyjątków - docs.python.org
try ... except
Oświadczenie ma opcjonalnąelse
klauzulę, która, jeśli jest obecny, musi przestrzegać wszystkich z wyjątkiem klauzul. Jest to przydatne w przypadku kodu, który należy wykonać, jeśli klauzula try nie zgłosi wyjątku. Na przykład:for arg in sys.argv[1:]: try: f = open(arg, 'r') except IOError: print 'cannot open', arg else: print arg, 'has', len(f.readlines()), 'lines' f.close()
Użycie klauzuli else jest lepsze niż dodanie dodatkowego kodu do klauzuli try, ponieważ pozwala uniknąć przypadkowego wyłapania wyjątku, który nie został zgłoszony przez kod chroniony przez instrukcję try ....
Patrząc na odniesienie do Pythona , wydaje się, że else
jest wykonywane po, try
gdy nie ma wyjątku. Opcjonalna klauzula else jest wykonywana, gdy kontrola wypływa z końca klauzuli try. 2 Wyjątki w klauzuli else nie są obsługiwane przez poprzednie klauzule oprócz.
Zanurz się w pythonie ma przykład, w którym, jeśli dobrze rozumiem, w try
bloku próbują zaimportować moduł, gdy to się nie powiedzie, otrzymasz wyjątek i powiąż domyślnie, ale gdy to działa, masz opcję, aby przejść doelse
bloku i powiązania tego, co jest wymagane (zobacz link do przykładu i objaśnienia).
Jeśli spróbujesz wykonać pracę w catch
bloku, może to rzucić kolejny wyjątek - myślę, że właśnie tam else
przydatny jest blok.
try
bloku.
Otóż to. Blok „else” klauzuli try-wyjątkiem istnieje dla kodu, który jest uruchamiany, gdy (i tylko wtedy), gdy próba się powiedzie. Można go używać i nadużywać.
try:
fp= open("configuration_file", "rb")
except EnvironmentError:
confdata= '' # it's ok if the file can't be opened
else:
confdata= fp.read()
fp.close()
# your code continues here
# working with (possibly empty) confdata
Osobiście podoba mi się i używam go w razie potrzeby. Semantycznie grupuje instrukcje.
Być może zastosowanie może być:
#debug = []
def debuglog(text, obj=None):
" Simple little logger. "
try:
debug # does global exist?
except NameError:
pass # if not, don't even bother displaying
except:
print('Unknown cause. Debug debuglog().')
else:
# debug does exist.
# Now test if you want to log this debug message
# from caller "obj"
try:
if obj in debug:
print(text) # stdout
except TypeError:
print('The global "debug" flag should be an iterable.')
except:
print('Unknown cause. Debug debuglog().')
def myfunc():
debuglog('Made it to myfunc()', myfunc)
debug = [myfunc,]
myfunc()
Może to też przyniesie ci korzyść.
Znalazłem try: ... else:
konstrukcję przydatną w sytuacji, gdy uruchamiasz zapytania do bazy danych i logujesz wyniki tych zapytań do osobnej bazy danych tego samego typu / smaku. Załóżmy, że mam wiele wątków roboczych wszystkich zapytań do bazy danych przesłanych do kolejki
#in a long running loop
try:
query = queue.get()
conn = connect_to_db(<main db>)
curs = conn.cursor()
try:
curs.execute("<some query on user input that may fail even if sanitized">)
except DBError:
logconn = connect_to_db(<logging db>)
logcurs = logconn.cursor()
logcurs.execute("<update in DB log with record of failed query")
logcurs.close()
logconn.close()
else:
#we can't put this in main try block because an error connecting
#to the logging DB would be indistinguishable from an error in
#the mainquery
#We can't put this after the whole try: except: finally: block
#because then we don't know if the query was successful or not
logconn = connect_to_db(<logging db>)
logcurs = logconn.cursor()
logcurs.execute("<update in DB log with record of successful query")
logcurs.close()
logconn.close()
#do something in response to successful query
except DBError:
#This DBError is because of a problem with the logging database, but
#we can't let that crash the whole thread over what might be a
#temporary network glitch
finally:
curs.close()
conn.close()
#other cleanup if necessary like telling the queue the task is finished
Oczywiście, jeśli potrafisz rozróżnić możliwe wyjątki, które mogą zostać zgłoszone, nie musisz tego używać, ale jeśli kod reagujący na udany fragment kodu może zgłosić ten sam wyjątek, co udany fragment, i nie możesz po prostu puść drugi możliwy wyjątek lub natychmiast powróć po sukcesie (co w moim przypadku zabiłoby wątek), to się przydaje.
else
Blok może często występować jako uzupełnienie funkcjonalności, który występuje w każdym except
bloku.
try:
test_consistency(valuable_data)
except Except1:
inconsistency_type = 1
except Except2:
inconsistency_type = 2
except:
# Something else is wrong
raise
else:
inconsistency_type = 0
"""
Process each individual inconsistency down here instead of
inside the except blocks. Use 0 to mean no inconsistency.
"""
W tym przypadku inconsistency_type
ustawiana jest w każdym bloku oprócz bloku, dzięki czemu zachowanie jest uzupełniane w przypadku braku błędu w else
.
Oczywiście opisuję to jako wzorzec, który może kiedyś pojawić się w twoim kodzie. W tym konkretnym przypadku po prostu ustawiasz inconsistency_type
0 przed try
blokiem.
Oto inne miejsce, w którym lubię używać tego wzoru:
while data in items:
try
data = json.loads(data)
except ValueError as e:
log error
else:
# work on the `data`
continue
- wzorca „wcześnie się wydostać”. Pozwala to na usunięcie klauzuli „else” i jej wcięcia, dzięki czemu kod jest łatwiejszy do odczytania.
Jednym ze scenariuszy użycia, który mogę wymyślić, są nieprzewidywalne wyjątki, które można obejść, jeśli spróbujesz ponownie. Na przykład, gdy operacje w bloku try obejmują losowe liczby:
while True:
try:
r = random.random()
some_operation_that_fails_for_specific_r(r)
except Exception:
continue
else:
break
Ale jeśli można przewidzieć wyjątek, zawsze należy wcześniej wybrać walidację zamiast wyjątku. Jednak nie wszystko można przewidzieć, więc ten wzorzec kodu ma swoje miejsce.
break
wnętrze try
na końcu, co jest czystszym IMO, i nie potrzebujesz else
. Również continue
nie jest tak naprawdę potrzebne, możesz po prostu pass
.
Uznałem, że else
przydatne jest postępowanie z potencjalnie niepoprawnym plikiem konfiguracyjnym:
try:
value, unit = cfg['lock'].split()
except ValueError:
msg = 'lock monitoring config must consist of two words separated by white space'
self.log('warn', msg)
else:
# get on with lock monitoring if config is ok
Wyjątek czytający lock
config wyłącza monitorowanie blokady, a ValueErrors rejestruje pomocny komunikat ostrzegawczy.
Dodałbym inny przypadek użycia, który wydaje się prosty podczas obsługi sesji DB:
# getting a DB connection
conn = db.engine.connect()
# and binding to a DB session
session = db.get_session(bind=conn)
try:
# we build the query to DB
q = session.query(MyTable).filter(MyTable.col1 == 'query_val')
# i.e retrieve one row
data_set = q.one_or_none()
# return results
return [{'col1': data_set.col1, 'col2': data_set.col2, ...}]
except:
# here we make sure to rollback the transaction,
# handy when we update stuff into DB
session.rollback()
raise
else:
# when no errors then we can commit DB changes
session.commit()
finally:
# and finally we can close the session
session.close()
else:
Blok jest mylące i (prawie) bezużyteczne. Jest to również część instrukcji for
i while
.
W rzeczywistości, nawet na podstawie oświadczenia if
, else:
można je nadużywać w naprawdę okropny sposób, tworząc bardzo trudne do znalezienia błędy.
Rozważ to.
if a < 10:
# condition stated explicitly
elif a > 10 and b < 10:
# condition confusing but at least explicit
else:
# Exactly what is true here?
# Can be hard to reason out what condition is true
Pomyśl dwa razy else:
. To generalnie problem. Unikaj tego, z wyjątkiem instrukcji if
-state, a nawet rozważ udokumentowanie else
warunku, aby wyrazić to wyraźnie.
if x > 0: return "yes"
i if x <= 0: return "no"
. Teraz ktoś przychodzi i zmienia jeden z warunków, x > 1
ale zapomina zmienić drugi. Jaka jest ta redukcja liczby błędów, które zostałyby popełnione. if else
klauzule mają czasem wiele wierszy od siebie. SUSZENIE to dobra praktyka, o wiele częściej niż nie. (przepraszam za podwójny post).