No trabalho, deparei-me com uma except
cláusula com um or
operador:
try:
# Do something.
except IndexError or KeyError:
# ErrorHandling
Eu sei que as classes de exceção devem ser passadas como uma tupla, mas me incomodou que nem causasse a SyntaxError
.
Então, primeiro eu queria investigar se realmente funciona. E isso não acontece.
>>> 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
Portanto, ele não capturou a segunda exceção e, olhando o bytecode, fica mais claro o motivo:
>>> 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
Como podemos ver, a instrução 14 primeiro carrega a IndexError
classe na pilha. Em seguida, verifica se esse valor é o True
que é devido à veracidade do Python e, por fim, pula diretamente para a instrução 20 onde exception match
é feito. Como a instrução 18 foi pulada, KeyError
nunca foi carregada na pilha e, portanto, não corresponde.
Eu tentei com Python 2.7 e 3.6, mesmo resultado.
Mas então, por que é uma sintaxe válida? Eu imagino que seja um dos seguintes:
- É um artefato de uma versão muito antiga do Python.
- Na verdade, existe um caso de uso válido para uso
or
em umaexcept
cláusula. - É simplesmente uma limitação do analisador Python que pode ter que aceitar qualquer expressão após a
except
palavra - chave.
Meu voto é em 3 (dado que vi algumas discussões sobre um novo analisador para Python), mas espero que alguém possa confirmar essa hipótese. Porque se fosse 2, por exemplo, eu quero saber esse caso de uso!
Além disso, sou um pouco ignorante sobre como eu continuaria essa exploração. Eu imagino que eu teria que cavar no código fonte do analisador CPython, mas idk onde encontrá-lo e talvez haja uma maneira mais fácil?
fonte
except
declaração.except IndexError or KeyError
parece uma coisa decente para escrever. No entanto, concordo com você que seria contra outros valores que o Python tenta respeitar.var == 1 or 2
, que para os olhos destreinados também "parece uma coisa decente para escrever".Você deve usar uma n-tupla de tipos em vez de uma expressão lógica (que retorna apenas o primeiro elemento não falso):
fonte
or
Python ainda era válido.