Python Language
Anty-wzory Pythona
Szukaj…
Nadgorliwy z wyjątkiem klauzuli
Wyjątki są potężne, ale jedna nadgorliwa, z wyjątkiem klauzuli, może zabrać to wszystko w jednym wierszu.
try: res = get_result() res = res[0] log('got result: %r' % res) except: if not res: res = '' print('got exception')
Ten przykład pokazuje 3 objawy antypatternu:
-
except
bez typu wyjątku (wiersz 5) wyłapie nawet zdrowe wyjątki, w tymKeyboardInterrupt
. W niektórych przypadkach uniemożliwi to zamknięcie programu. - Blok wyjątków nie zwraca błędu, co oznacza, że nie będziemy w stanie stwierdzić, czy wyjątek pochodzi z
get_result
czy dlatego, żeres
była pustą listą. - Co najgorsze, jeśli martwiliśmy się, że wynik będzie pusty, spowodowaliśmy coś znacznie gorszego. Jeśli
get_result
nie powiedzie,res
pozostanie całkowicie nieuzbrojony, a odwołanie dores
w blokuNameError
podniesieNameError
, całkowicie maskując pierwotny błąd.
Zawsze myśl o typie wyjątku, który próbujesz obsłużyć. Przeczytaj stronę wyjątków i dowiedz się, jakie są podstawowe wyjątki.
Oto stała wersja powyższego przykładu:
import traceback try: res = get_result() except Exception: log_exception(traceback.format_exc()) raise try: res = res[0] except IndexError: res = '' log('got result: %r' % res)
Łapiemy bardziej szczegółowe wyjątki, w razie potrzeby dokonując zmian. Jeszcze kilka linii, ale nieskończenie bardziej poprawnych.
Patrząc przed skokiem dzięki funkcji intensywnie wykorzystującej procesor
Program może łatwo tracić czas, wywołując wielokrotnie funkcję wymagającą dużej mocy procesora.
Weźmy na przykład funkcję, która wygląda tak: zwraca liczbę całkowitą, jeśli value
wejściowa może ją wytworzyć, w przeciwnym razie None
:
def intensive_f(value): # int -> Optional[int]
# complex, and time-consuming code
if process_has_failed:
return None
return integer_output
I można go użyć w następujący sposób:
x = 5
if intensive_f(x) is not None:
print(intensive_f(x) / 2)
else:
print(x, "could not be processed")
print(x)
Chociaż to zadziała, ma problem z wywołaniem intensive_f
, co podwaja czas działania kodu. Lepszym rozwiązaniem byłoby wcześniejsze uzyskanie wartości zwracanej przez funkcję.
x = 5
result = intensive_f(x)
if result is not None:
print(result / 2)
else:
print(x, "could not be processed")
Jednak wyraźniejszym i prawdopodobnie bardziej pythonicznym sposobem jest użycie wyjątków, na przykład:
x = 5
try:
print(intensive_f(x) / 2)
except TypeError: # The exception raised if None + 1 is attempted
print(x, "could not be processed")
Tutaj nie jest wymagana zmienna tymczasowa. Często może być korzystne użycie assert
oświadczenie i złapać AssertionError
zamiast.
Klucze słownika
Typowym przykładem tego, gdzie można to znaleźć, jest dostęp do kluczy słownika. Na przykład porównaj:
bird_speeds = get_very_long_dictionary()
if "european swallow" in bird_speeds:
speed = bird_speeds["european swallow"]
else:
speed = input("What is the air-speed velocity of an unladen swallow?")
print(speed)
z:
bird_speeds = get_very_long_dictionary()
try:
speed = bird_speeds["european swallow"]
except KeyError:
speed = input("What is the air-speed velocity of an unladen swallow?")
print(speed)
Pierwszy przykład musi dwukrotnie przejrzeć słownik, a ponieważ jest to długi słownik, za każdym razem może to zająć dużo czasu. Drugi wymaga tylko jednego wyszukiwania w słowniku, a tym samym oszczędza dużo czasu procesora.
Alternatywą jest użycie dict.get(key, default)
, jednak wiele okoliczności może wymagać bardziej złożonych operacji w przypadku braku klucza