W pracy natknąłem się na except
klauzulę z or
operatorem:
try:
# Do something.
except IndexError or KeyError:
# ErrorHandling
Wiem, że klasy wyjątków powinny być przekazywane jako krotka, ale wkurzyło mnie to, że nawet nie spowoduje SyntaxError
.
Więc najpierw chciałem sprawdzić, czy to rzeczywiście działa. I tak nie jest.
>>> def with_or_raise(exc):
... try:
... raise exc()
... except IndexError or KeyError:
... print('Got ya!')
...
>>> with_or_raise(IndexError)
Got ya!
>>> with_or_raise(KeyError)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in with_or_raise
KeyError
Więc nie złapał drugiego wyjątku, a patrząc na kod bajtowy, staje się wyraźniejsze, dlaczego:
>>> import dis
>>> dis.dis(with_or_raise)
2 0 SETUP_EXCEPT 10 (to 12)
3 2 LOAD_FAST 0 (exc)
4 CALL_FUNCTION 0
6 RAISE_VARARGS 1
8 POP_BLOCK
10 JUMP_FORWARD 32 (to 44)
4 >> 12 DUP_TOP
14 LOAD_GLOBAL 0 (IndexError)
16 JUMP_IF_TRUE_OR_POP 20
18 LOAD_GLOBAL 1 (KeyError)
>> 20 COMPARE_OP 10 (exception match)
22 POP_JUMP_IF_FALSE 42
24 POP_TOP
26 POP_TOP
28 POP_TOP
5 30 LOAD_GLOBAL 2 (print)
32 LOAD_CONST 1 ('Got ya!')
34 CALL_FUNCTION 1
36 POP_TOP
38 POP_EXCEPT
40 JUMP_FORWARD 2 (to 44)
>> 42 END_FINALLY
>> 44 LOAD_CONST 0 (None)
46 RETURN_VALUE
Jak widzimy, instrukcja 14 najpierw ładuje IndexError
klasę na stos. Następnie sprawdza, czy jest to wartość True
, która wynika z prawdomówności Pythona, i ostatecznie przeskakuje bezpośrednio do instrukcji 20, w której exception match
jest wykonywana. Ponieważ instrukcja 18 została pominięta, KeyError
nigdy nie została załadowana na stos i dlatego nie pasuje.
Próbowałem z Python 2.7 i 3.6, ten sam wynik.
Ale dlaczego jest to poprawna składnia? Wyobrażam sobie, że jest to jeden z następujących:
- To artefakt z naprawdę starej wersji Pythona.
- W rzeczywistości istnieje poprawny przypadek użycia
or
w ramachexcept
klauzuli. - Jest to po prostu ograniczenie parsera Pythona, który może wymagać zaakceptowania dowolnego wyrażenia po
except
słowie kluczowym.
Mój głos jest na 3 (biorąc pod uwagę, że widziałem dyskusję na temat nowego parsera dla Pythona), ale mam nadzieję, że ktoś potwierdzi tę hipotezę. Bo jeśli na przykład było to 2, chcę poznać ten przypadek użycia!
Poza tym jestem trochę nieświadomy tego, jak kontynuowałbym tę eksplorację. Wyobrażam sobie, że musiałbym zagłębić się w kod źródłowy parsera CPython, ale nie wiem gdzie go znaleźć, a może jest łatwiejszy sposób?