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:

  1. except bez typu wyjątku (wiersz 5) wyłapie nawet zdrowe wyjątki, w tym KeyboardInterrupt . W niektórych przypadkach uniemożliwi to zamknięcie programu.
  2. 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, że res była pustą listą.
  3. 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 do res w bloku NameError podniesie NameError , 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



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow