Introdução
Embora as exceções tenham sido introduzidas anteriormente, esta seção preenche alguns detalhes adicionais sobre a verificação de erros e o tratamento de exceções (exception handling).
This tutorial is from open-source community. Access the source code
Embora as exceções tenham sido introduzidas anteriormente, esta seção preenche alguns detalhes adicionais sobre a verificação de erros e o tratamento de exceções (exception handling).
Python não realiza nenhuma verificação ou validação dos tipos ou valores dos argumentos de função. Uma função funcionará em quaisquer dados que sejam compatíveis com as instruções na função.
def add(x, y):
return x + y
add(3, 4) ## 7
add('Hello', 'World') ## 'HelloWorld'
add('3', '4') ## '34'
Se houver erros em uma função, eles aparecem em tempo de execução (como uma exceção).
def add(x, y):
return x + y
>>> add(3, '4')
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +:
'int' and 'str'
>>>
Para verificar o código, há uma forte ênfase em testes (abordados posteriormente).
Exceções são usadas para sinalizar erros. Para lançar uma exceção (raise an exception) você mesmo, use a instrução raise.
if name not in authorized:
raise RuntimeError(f'{name} not authorized')
Para capturar uma exceção, use try-except.
try:
authenticate(username)
except RuntimeError as e:
print(e)
Exceções propagam-se para o primeiro except correspondente.
def grok():
...
raise RuntimeError('Whoa!') ## Exception raised here
def spam():
grok() ## Call that will raise exception
def bar():
try:
spam()
except RuntimeError as e: ## Exception caught here
...
def foo():
try:
bar()
except RuntimeError as e: ## Exception does NOT arrive here
...
foo()
Para tratar a exceção, coloque as instruções no bloco except. Você pode adicionar quaisquer instruções que desejar para tratar o erro.
def grok(): ...
raise RuntimeError('Whoa!')
def bar():
try:
grok()
except RuntimeError as e: ## Exception caught here
statements ## Use this statements
statements
...
bar()
Após o tratamento, a execução continua com a primeira instrução após o try-except.
def grok(): ...
raise RuntimeError('Whoa!')
def bar():
try:
grok()
except RuntimeError as e: ## Exception caught here
statements
statements
...
statements ## Resumes execution here
statements ## And continues here
...
bar()
Existem cerca de duas dúzias de exceções embutidas. Normalmente, o nome da exceção é indicativo do que está errado (por exemplo, um ValueError é lançado porque você forneceu um valor inválido). Esta não é uma lista exaustiva. Consulte a documentação para mais informações.
ArithmeticError
AssertionError
EnvironmentError
EOFError
ImportError
IndexError
KeyboardInterrupt
KeyError
MemoryError
NameError
ReferenceError
RuntimeError
SyntaxError
SystemError
TypeError
ValueError
Exceções têm um valor associado. Ele contém informações mais específicas sobre o que está errado.
raise RuntimeError('Invalid user name')
Este valor faz parte da instância da exceção que é colocada na variável fornecida ao except.
try:
...
except RuntimeError as e: ## `e` holds the exception raised
...
e é uma instância do tipo de exceção. No entanto, muitas vezes se parece com uma string quando impresso.
except RuntimeError as e:
print('Failed : Reason', e)
Você pode capturar diferentes tipos de exceções usando múltiplos blocos except.
try:
...
except LookupError as e:
...
except RuntimeError as e:
...
except IOError as e:
...
except KeyboardInterrupt as e:
...
Alternativamente, se as instruções para tratá-las forem as mesmas, você pode agrupá-las:
try:
...
except (IOError,LookupError,RuntimeError) as e:
...
Para capturar qualquer exceção, use Exception desta forma:
try:
...
except Exception: ## DANGER. See below
print('An error occurred')
Em geral, escrever código assim é uma má ideia porque você não terá ideia do porquê falhou.
Aqui está a maneira errada de usar exceções.
try:
go_do_something()
except Exception:
print('Computer says no')
Isso captura todos os erros possíveis e pode tornar impossível a depuração quando o código falha por alguma razão que você não esperava (por exemplo, módulo Python desinstalado, etc.).
Se você vai capturar todos os erros, esta é uma abordagem mais sensata.
try:
go_do_something()
except Exception as e:
print('Computer says no. Reason :', e)
Ele relata uma razão específica para a falha. É quase sempre uma boa ideia ter algum mecanismo para visualizar/relatar erros quando você escreve código que captura todas as exceções possíveis.
Em geral, no entanto, é melhor capturar o erro da forma mais restrita possível. Capture apenas os erros que você realmente pode tratar. Deixe outros erros passarem – talvez algum outro código possa tratá-los.
Use raise para propagar um erro capturado.
try:
go_do_something()
except Exception as e:
print('Computer says no. Reason :', e)
raise
Isso permite que você tome uma ação (por exemplo, logging) e passe o erro para quem chamou a função.
Não capture exceções. Falhe rápido e alto. Se for importante, outra pessoa cuidará do problema. Capture uma exceção apenas se você for essa pessoa. Ou seja, capture apenas erros onde você pode se recuperar e continuar de forma sensata.
finallyEspecifica o código que deve ser executado, independentemente de uma exceção ocorrer ou não.
lock = Lock()
...
lock.acquire()
try:
...
finally:
lock.release() ## this will ALWAYS be executed. With and without exception.
Comumente usado para gerenciar recursos com segurança (especialmente locks, arquivos, etc.).
withEm código moderno, try-finally é frequentemente substituído pela declaração with.
lock = Lock()
with lock:
## lock acquired
...
## lock released
Um exemplo mais familiar:
with open(filename) as f:
## Use the file
...
## File closed
with define um contexto de uso para um recurso. Quando a execução sai desse contexto, os recursos são liberados. with só funciona com certos objetos que foram especificamente programados para suportá-lo.
A função parse_csv() que você escreveu na seção anterior permite que colunas especificadas pelo usuário sejam selecionadas, mas isso só funciona se o arquivo de dados de entrada tiver cabeçalhos de coluna.
Modifique o código para que uma exceção seja levantada se os argumentos select e has_headers=False forem passados. Por exemplo:
>>> parse_csv('/home/labex/project/prices.csv', select=['name','price'], has_headers=False)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "fileparse.py", line 9, in parse_csv
raise RuntimeError("select argument requires column headers")
RuntimeError: select argument requires column headers
>>>
Tendo adicionado essa verificação, você pode se perguntar se deveria estar realizando outros tipos de verificações de sanidade na função. Por exemplo, você deve verificar se o nome do arquivo é uma string, se types é uma lista, ou algo dessa natureza?
Como regra geral, geralmente é melhor pular esses testes e apenas deixar o programa falhar em entradas ruins. A mensagem de traceback apontará para a origem do problema e pode auxiliar na depuração.
A principal razão para adicionar a verificação acima é evitar a execução do código em um modo sem sentido (por exemplo, usando um recurso que requer cabeçalhos de coluna, mas simultaneamente especificando que não há cabeçalhos).
Isso indica um erro de programação por parte do código de chamada. Verificar casos que "não deveriam acontecer" é frequentemente uma boa ideia.
A função parse_csv() que você escreveu é usada para processar todo o conteúdo de um arquivo. No entanto, no mundo real, é possível que os arquivos de entrada tenham dados corrompidos, ausentes ou sujos. Tente este experimento:
>>> portfolio = parse_csv('missing.csv', types=[str, int, float])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "fileparse.py", line 36, in parse_csv
row = [func(val) for func, val in zip(types, row)]
ValueError: invalid literal for int() with base 10: ''
>>>
Modifique a função parse_csv() para capturar todas as exceções ValueError geradas durante a criação do registro e imprimir uma mensagem de aviso para as linhas que não podem ser convertidas.
A mensagem deve incluir o número da linha e informações sobre o motivo da falha. Para testar sua função, tente ler o arquivo missing.csv acima. Por exemplo:
>>> portfolio = parse_csv('missing.csv', types=[str, int, float])
Row 4: Couldn't convert ['MSFT', '', '51.23']
Row 4: Reason invalid literal for int() with base 10: ''
Row 7: Couldn't convert ['IBM', '', '70.44']
Row 7: Reason invalid literal for int() with base 10: ''
>>>
>>> portfolio
[{'name': 'AA', 'shares': 100, 'price': 32.2}, {'name': 'IBM', 'shares': 50, 'price': 91.1}, {'name': 'CAT', 'shares': 150, 'price': 83.44}, {'name': 'GE', 'shares': 95, 'price': 40.37}, {'name': 'MSFT', 'shares': 50, 'price': 65.1}]
>>>
Modifique a função parse_csv() para que as mensagens de erro de parsing possam ser silenciadas se explicitamente desejado pelo usuário. Por exemplo:
>>> portfolio = parse_csv('missing.csv', types=[str,int,float], silence_errors=True)
>>> portfolio
[{'name': 'AA', 'shares': 100, 'price': 32.2}, {'name': 'IBM', 'shares': 50, 'price': 91.1}, {'name': 'CAT', 'shares': 150, 'price': 83.44}, {'name': 'GE', 'shares': 95, 'price': 40.37}, {'name': 'MSFT', 'shares': 50, 'price': 65.1}]
>>>
O tratamento de erros é uma das coisas mais difíceis de acertar na maioria dos programas. Como regra geral, você não deve ignorar erros silenciosamente. Em vez disso, é melhor relatar problemas e dar ao usuário a opção de silenciar a mensagem de erro, caso ele escolha fazê-lo.
Parabéns! Você concluiu o laboratório de Verificação de Erros (Error Checking). Você pode praticar mais laboratórios no LabEx para aprimorar suas habilidades.