8. Erros e exceções¶
Até agora mensagens de erro foram apenas mencionadas, mas se você testou os exemplos, talvez tenha esbarrado em algumas. Existem pelo menos dois tipos distintos de erros: erros de sintaxe e exceções.
8.1. Erros de sintaxe¶
Erros de sintaxe, também conhecidos como erros de parse, são provavelmente os mais frequentes entre aqueles que ainda estão aprendendo Python:
>>> while True print('Hello world')
File "<stdin>", line 1
while True print('Hello world')
^
SyntaxError: invalid syntax
O parser repete a linha inválida e apresenta uma pequena ‘seta’ apontando para o ponto da linha em que o erro foi detectado. O erro é causado (ou ao menos detectado) pelo símbolo que precede a seta: no exemplo, o erro foi detectado na função print()
, uma vez que um dois-pontos (':'
) está faltando antes dela. O nome de arquivo e número de linha são exibidos para que você possa rastrear o erro no texto do script.
8.2. Exceções¶
Mesmo que um comando ou expressão estejam sintaticamente corretos, talvez ocorra um erro na hora de sua execução. Erros detectados durante a execução são chamados exceções e não são necessariamente fatais: logo veremos como tratá-las em programas Python. A maioria das exceções não são tratadas pelos programas e acabam resultando em mensagens de erro:
>>> 10 * (1/0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str
A última linha da mensagem de erro indica o que aconteceu. Exceções surgem com diferentes tipos, e o tipo é exibido como parte da mensagem: os tipos no exemplo são ZeroDivisionError
, NameError
e TypeError
. A string exibida como sendo o tipo da exceção é o nome da exceção embutida que ocorreu. Isso é verdade para todas exceções pré-definidas em Python, mas não é necessariamente verdade para exceções definidas pelo usuário (embora seja uma convenção útil). Os nomes das exceções padrões são identificadores embutidos (não palavras reservadas).
O resto da linha é um detalhamento que depende do tipo da exceção ocorrida e sua causa.
A parte anterior da mensagem de erro apresenta o contexto onde ocorreu a exceção. Essa informação é denominada stack traceback (situação da pilha de execução). Em geral, contém uma lista de linhas do código-fonte, sem apresentar, no entanto, linhas lidas da entrada padrão.
Exceções embutidas lista as exceções pré-definidas e seus significados.
8.3. Tratamento de exceções¶
É possível escrever programas que tratam exceções específicas. Observe o exemplo seguinte, que pede dados ao usuário até que um inteiro válido seja fornecido, ainda permitindo que o programa seja interrompido (utilizando Control-C ou seja lá o que for que o sistema operacional suporte); note que uma interrupção gerada pelo usuário será sinalizada pela exceção KeyboardInterrupt
.
>>> while True:
... try:
... x = int(input("Please enter a number: "))
... break
... except ValueError:
... print("Oops! That was no valid number. Try again...")
...
A instrução try
funciona da seguinte maneira:
Primeiramente, a cláusula try (o conjunto de instruções entre as palavras reservadas
try
eexcept
) é executada.Se nenhuma exceção ocorrer, a cláusula except é ignorada e a execução da instrução
try
é finalizada.If an exception occurs during execution of the try clause, the rest of the clause is skipped. Then if its type matches the exception named after the
except
keyword, the except clause is executed, and then execution continues after thetry
statement.If an exception occurs which does not match the exception named in the except clause, it is passed on to outer
try
statements; if no handler is found, it is an unhandled exception and execution stops with a message as shown above.
A try
statement may have more than one except clause, to specify
handlers for different exceptions. At most one handler will be executed.
Handlers only handle exceptions that occur in the corresponding try clause, not
in other handlers of the same try
statement. An except clause may
name multiple exceptions as a parenthesized tuple, for example:
... except (RuntimeError, TypeError, NameError):
... pass
A class in an except
clause is compatible with an exception if it is
the same class or a base class thereof (but not the other way around — an
except clause listing a derived class is not compatible with a base class). For
example, the following code will print B, C, D in that order:
class B(Exception):
pass
class C(B):
pass
class D(C):
pass
for cls in [B, C, D]:
try:
raise cls()
except D:
print("D")
except C:
print("C")
except B:
print("B")
Note that if the except clauses were reversed (with except B
first), it
would have printed B, B, B — the first matching except clause is triggered.
The last except clause may omit the exception name(s), to serve as a wildcard. Use this with extreme caution, since it is easy to mask a real programming error in this way! It can also be used to print an error message and then re-raise the exception (allowing a caller to handle the exception as well):
import sys
try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except OSError as err:
print("OS error: {0}".format(err))
except ValueError:
print("Could not convert data to an integer.")
except:
print("Unexpected error:", sys.exc_info()[0])
raise
The try
… except
statement has an optional else
clause, which, when present, must follow all except clauses. It is useful for
code that must be executed if the try clause does not raise an exception. For
example:
for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
except OSError:
print('cannot open', arg)
else:
print(arg, 'has', len(f.readlines()), 'lines')
f.close()
É melhor usar a cláusula else
do que adicionar código adicional à cláusula try
porque ela evita que acidentalmente seja tratada uma exceção que não foi levantada pelo código protegido pela construção com as instruções try
… except
.
Quando uma exceção ocorre, ela pode estar associada a um valor chamado argumento da exceção. A presença e o tipo do argumento dependem do tipo da exceção.
The except clause may specify a variable after the exception name. The
variable is bound to an exception instance with the arguments stored in
instance.args
. For convenience, the exception instance defines
__str__()
so the arguments can be printed directly without having to
reference .args
. One may also instantiate an exception first before
raising it and add any attributes to it as desired.
>>> try:
... raise Exception('spam', 'eggs')
... except Exception as inst:
... print(type(inst)) # the exception instance
... print(inst.args) # arguments stored in .args
... print(inst) # __str__ allows args to be printed directly,
... # but may be overridden in exception subclasses
... x, y = inst.args # unpack args
... print('x =', x)
... print('y =', y)
...
<class 'Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs
Caso uma exceção tenha argumentos, os mesmos serão impressos como a última parte (‘detalhe’) da mensagem para as exceções não tratadas.
Exception handlers don’t just handle exceptions if they occur immediately in the try clause, but also if they occur inside functions that are called (even indirectly) in the try clause. For example:
>>> def this_fails():
... x = 1/0
...
>>> try:
... this_fails()
... except ZeroDivisionError as err:
... print('Handling run-time error:', err)
...
Handling run-time error: division by zero
8.4. Levantando exceções¶
A instrução raise
permite ao programador forçar a ocorrência de um determinado tipo de exceção. Por exemplo:
>>> raise NameError('HiThere')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: HiThere
O argumento de raise
indica a exceção a ser levantada. Esse argumento deve ser uma instância de exceção ou uma classe de exceção (uma classe que deriva de Exception
). Se uma classe de exceção for passada, será implicitamente instanciada invocando o seu construtor sem argumentos:
raise ValueError # shorthand for 'raise ValueError()'
Caso você precise determinar se uma exceção foi levantada ou não, mas não quer manipular o erro, uma forma simples de instrução raise
permite que você levante-a novamente:
>>> try:
... raise NameError('HiThere')
... except NameError:
... print('An exception flew by!')
... raise
...
An exception flew by!
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
NameError: HiThere
8.5. Encadeamento de exceções¶
The raise
statement allows an optional from
which enables
chaining exceptions. For example:
# exc must be exception instance or None.
raise RuntimeError from exc
Isso pode ser útil quando você está transformando exceções. Por exemplo:
>>> def func():
... raise IOError
...
>>> try:
... func()
... except IOError as exc:
... raise RuntimeError('Failed to open database') from exc
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 2, in func
OSError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError: Failed to open database
Exception chaining happens automatically when an exception is raised inside an
except
or finally
section. Exception chaining can be
disabled by using from None
idiom:
>>> try:
... open('database.sqlite')
... except OSError:
... raise RuntimeError from None
...
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError
Para mais informações sobre os mecanismos de encadeamento, veja Exceções embutidas.
8.6. Exceções definidas pelo usuário¶
Programas podem definir novos tipos de exceções, através da criação de uma nova classe (veja Classes para mais informações sobre classes Python). Exceções devem ser derivadas da classe Exception
, direta ou indiretamente.
As classes de exceção podem ser definidas para fazer qualquer coisa que qualquer outra classe pode fazer, mas geralmente são mantidas simples, geralmente oferecendo apenas um número de atributos que permitem que informações sobre o erro sejam extraídas por manipuladores para a exceção.
É comum que novas exceções sejam definidas com nomes terminando em “Error”, semelhante a muitas exceções embutidas.
Muitos módulos padrão definem novas exceções para reportar erros que ocorrem no interior das funções que definem. Mais informações sobre classes aparecem no capítulo Classes.
8.7. Definindo ações de limpeza¶
A instrução try
possui outra cláusula opcional, cuja finalidade é permitir a implementação de ações de limpeza, que sempre devem ser executadas independentemente da ocorrência de exceções. Como no exemplo:
>>> try:
... raise KeyboardInterrupt
... finally:
... print('Goodbye, world!')
...
Goodbye, world!
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
KeyboardInterrupt
Se uma cláusula finally
estiver presente, a cláusula finally
será executada como a última tarefa antes da conclusão da instrução try
. A cláusula finally
executa se a instrução try
produz uma exceção. Os pontos a seguir discutem casos mais complexos quando ocorre uma exceção:
Se ocorrer uma exceção durante a execução da cláusula
try
, a exceção poderá ser tratada por uma cláusulaexcept
. Se a exceção não for tratada por uma cláusulaexcept
, a exceção será gerada novamente após a execução da cláusulafinally
.Uma exceção pode ocorrer durante a execução de uma cláusula
except
ouelse
. Novamente, a exceção é re-levantada depois quefinally
é executada.Se a cláusula
finally
executa uma instruçãobreak
,continue
oureturn
, as exceções não são levantadas novamente.Se a instrução
try
atingir uma instruçãobreak
,continue
oureturn
, a cláusulafinally
será executada imediatamente antes da execução da instruçãobreak
,continue
oureturn
.Se uma cláusula
finally
incluir uma instruçãoreturn
, o valor retornado será aquele da instruçãoreturn
da cláusulafinally
, não o valor da instruçãoreturn
da cláusulatry
.
Por exemplo:
>>> def bool_return():
... try:
... return True
... finally:
... return False
...
>>> bool_return()
False
Um exemplo mais complicado:
>>> def divide(x, y):
... try:
... result = x / y
... except ZeroDivisionError:
... print("division by zero!")
... else:
... print("result is", result)
... finally:
... print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'
Como você pode ver, a cláusula finally
é executada em todos os casos. A exceção TypeError
levantada pela divisão de duas strings não é tratada pela cláusula except
e portanto é re-levantada depois que a cláusula finally
é executada.
Em aplicação do mundo real, a cláusula finally
é útil para liberar recursos externos (como arquivos ou conexões de rede), independentemente do uso do recurso ter sido bem sucedido ou não.
8.8. Ações de limpeza predefinidas¶
Alguns objetos definem ações de limpeza padrões para serem executadas quando o objeto não é mais necessário, independentemente da operação que estava usando o objeto ter sido ou não bem sucedida. Veja o exemplo a seguir, que tenta abrir um arquivo e exibir seu conteúdo na tela.
for line in open("myfile.txt"):
print(line, end="")
O problema com esse código é que ele deixa o arquivo aberto um período indeterminado depois que o código é executado. Isso não chega a ser problema em scripts simples, mas pode ser um problema para grandes aplicações. A palavra reservada with
permite que objetos como arquivos sejam utilizados com a certeza de que sempre serão prontamente e corretamente finalizados.
with open("myfile.txt") as f:
for line in f:
print(line, end="")
Depois que a instrução é executada, o arquivo f é sempre fechado, mesmo se ocorrer um problema durante o processamento das linhas. Outros objetos que, como arquivos, fornecem ações de limpeza predefinidas as indicarão em suas documentações.