Articles

Excepção e Tratamento de Erros em Python

Introduction


Source

Antes de entrarmos no porquê do tratamento de excepções ser essencial e tipos de construção…em excepções que a Python apoia, é necessário compreender que existe uma diferença subtil entre um error e um exception.

Errores não podem ser tratados, enquanto as excepções Python podem ser tratadas no tempo de execução. Um erro pode ser um erro syntax (parsing), enquanto que pode haver muitos tipos de excepções que podem ocorrer durante a execução e não são incondicionalmente inoperáveis. Um Error poderia indicar problemas críticos que uma aplicação razoável não deveria tentar apanhar, enquanto um Exception poderia indicar condições que uma aplicação deveria tentar apanhar. Os erros são uma forma de excepção não verificada e são irrecuperáveis como um OutOfMemoryError, que um programador não deve tentar tratar.

A manipulação de excepções torna o seu código mais robusto e ajuda a prevenir potenciais falhas que fariam com que o seu programa parasse de uma forma descontrolada. Imagine que se tivesse escrito um código que é implantado na produção e, mesmo assim, termina devido a uma excepção, o seu cliente não apreciaria isso, pelo que é melhor tratar da excepção em particular de antemão e evitar o caos.

Errores podem ser de vários tipos:

  • Erro de Sintaxe
  • Sair do Erro de Memória
  • Erro de Regressão
  • Excepções

Vamos vê-los um a um.

Syntax Error

Erros de sintaxe frequentemente chamados erros de análise, são causados predominantemente quando o analisador detecta um problema sintáctico no seu código.

Vamos dar um exemplo para o compreender.

a = 8b = 10c = a b
 File "<ipython-input-8-3b3ffcedf995>", line 3 c = a b ^SyntaxError: invalid syntax

A seta acima indica quando o analisador encontrou um erro durante a execução do código. O símbolo que precede a seta causa a falha. Para rectificar tais erros fundamentais, Python fará a maior parte do seu trabalho uma vez que imprimirá para si o nome do ficheiro e o número de linha em que o erro ocorreu.

Out of Memory Error

Memory errors are mostly dependent on your systems RAM and are related to Heap. Se tiver grandes objectos (ou) objectos referenciados na memória, então verá OutofMemoryError (Fonte). Pode ser causado devido a várias razões:

  • Utilizar uma Arquitectura Python de 32-bit (a alocação máxima de memória dada é muito baixa, entre 2GB – 4GB).
  • Carregar um ficheiro de dados muito grande
  • Executar um modelo de Aprendizagem de Máquina/Deep Learning e muito mais.

P>Pode tratar o erro memory com a ajuda do tratamento de excepções, uma excepção para quando o intérprete ficar sem memória e tiver de parar imediatamente a execução actual. Nestes raros casos, Python levanta um OutofMemoryError, permitindo ao script de alguma forma catch ele próprio e break sair do erro de memória e recuperar-se a si próprio.

No entanto, uma vez que Python adopta para a arquitectura de gestão de memória da linguagem C (função malloc()), não é certo que todos os processos do script irão recuperar – em alguns casos, um MemoryError resultará num crash irrecuperável. Assim, nem é uma boa prática usar o tratamento de excepções para tal erro, nem é aconselhável.

Erro de Recurssão

Está relacionado com stack e ocorre quando se chama funções. Como o nome sugere, recursion erro ocorre quando demasiados métodos, um dentro do outro é executado (um com uma repetição infinita), que é limitado pelo tamanho da pilha.

Todas as suas variáveis locais e métodos chamam os dados associados serão colocados na pilha. Para cada chamada de método, será criada uma moldura de pilha, e os dados locais, bem como a chamada de método, serão colocados dentro dessa moldura de pilha. Uma vez concluída a execução do método, a moldura da pilha será removida.

Para reproduzir este erro, vamos definir uma função recursion que será recursiva, o que significa que continuará a chamar-se a si própria como uma chamada de método de loop infinito, verá StackOverflow ou um Erro de Recurssão porque a moldura da pilha será preenchida com dados do método para cada chamada, mas não será libertada.

def recursion(): return recursion()
recursion()
---------------------------------------------------------------------------RecursionError Traceback (most recent call last)<ipython-input-3-c6e0f7eb0cde> in <module>----> 1 recursion()<ipython-input-2-5395140f7f05> in recursion() 1 def recursion():----> 2 return recursion()... last 1 frames repeated, from the frame below ...<ipython-input-2-5395140f7f05> in recursion() 1 def recursion():----> 2 return recursion()RecursionError: maximum recursion depth exceeded

Erro de Indentação

Erro de Indentação é semelhante em espírito ao erro de sintaxe e cai sob ele. Contudo, específico para as únicas questões relacionadas com o indentação no script.

P>P>Ponhamos um exemplo rápido para compreender um erro de indentação.

for i in range(10):print('Hello world')
 File "<ipython-input-6-628f419d2da8>", line 2 print('Hello world') ^IndentationError: expected an indented block

Excepções

P>Even se a sintaxe de uma declaração ou expressão estiver correcta, ainda pode causar um erro quando executada. As excepções Python são erros que são detectados durante a execução e não são incondicionalmente fatais: em breve aprenderá no tutorial como lidar com eles em programas Python. Um objecto de excepção é criado quando um script Python levanta uma excepção. Se o script não tratar explicitamente da excepção, o programa será forçado a terminar abruptamente.

Os programas normalmente não lidam com excepções, e resultam em mensagens de erro, como aqui mostrado:

Type Error

a = 2b = 'DataCamp'a + b
---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-7-86a706a0ffdf> in <module> 1 a = 2 2 b = 'DataCamp'----> 3 a + bTypeError: unsupported operand type(s) for +: 'int' and 'str'

Erro de Divisão Zero

100 / 0
---------------------------------------------------------------------------ZeroDivisionError Traceback (most recent call last)<ipython-input-43-e9e866a10e2a> in <module>----> 1 100 / 0ZeroDivisionError: division by zero

Existem vários tipos de excepções Python, e o tipo é impresso como parte da mensagem: os tipos nos dois exemplos acima são ZeroDivisionError e TypeError. Ambas as cadeias de erro impressas como tipo de excepção são o nome da excepção incorporada em Python.

A parte restante da linha de erro fornece os detalhes do que causou o erro com base no tipo de excepção.

Vejamos agora as excepções incorporadas em Python.

Built-in Exceptions


Source

Antes de começar a aprender as excepções incorporadas, vamos apenas rever rapidamente os quatro componentes principais do tratamento de excepções, como mostrado nesta figura.

  • Try: Irá executar o bloco de código em que espera que ocorra um erro.
  • Excepto: Aqui, irá definir o tipo de excepção que espera no bloco de tentativa (incorporada ou personalizada).
  • Else: Se não houver qualquer excepção, então este bloco de código será executado (considere isto como um remédio ou uma opção de recurso se espera que uma parte do seu script produza uma excepção).
  • Finalmente: Independentemente de existir ou não uma excepção, este bloco de código será sempre executado.

Na secção seguinte do tutorial, aprenderá sobre o tipo comum de excepções e também aprenderá a tratá-las com a ajuda do tratamento de excepções.

Keyboard Interrupt Error

A excepção KeyboardInterrupt é levantada quando se tenta parar um programa em execução premindo ctrl+c ou ctrl+z numa linha de comando ou interrompendo o kernel no Bloco de Notas Jupyter. Por vezes, pode não pretender interromper um programa, mas por engano, isso acontece, caso em que o tratamento de excepções para evitar tais problemas pode ser útil.

No exemplo abaixo, se executar a célula e interromper o kernel, o programa levantará uma excepção KeyboardInterrupt.inp = input()Vamos agora tratar do KeyboardInterrupt excepção.

try: inp = input() print ('Press Ctrl+C or Interrupt the Kernel:')except KeyboardInterrupt: print ('Caught KeyboardInterrupt')else: print ('No exception occurred')

Caught KeyboardInterrupt

Erro Padrão

Vamos conhecer alguns dos erros padrão que normalmente poderiam ocorrer durante a programação.

Erro Aritmético

  • Erro de Divisão Zero
  • Erro de Sobrefluxo
  • Erro de Ponto Flutuante

Todas as excepções acima mencionadas enquadram-se na classe base Arithmetic e são levantadas para erros em operações aritméticas, como aqui discutido.

Divisão Zero

Quando o divisor (segundo argumento da divisão) ou o denominador é zero, então o resultante levanta um erro de divisão zero.

try: a = 100 / 0 print (a)except ZeroDivisionError: print ("Zero Division Exception Raised." )else: print ("Success, no error!")
Zero Division Exception Raised.

Over erro de divisão

O erro de divisão é levantado quando o resultado de uma operação aritmética está fora de alcance. O OverflowError é levantado para inteiros que estão fora de um intervalo requerido.

try: import math print(math.exp(1000))except OverflowError: print ("OverFlow Exception Raised.")else: print ("Success, no error!")
OverFlow Exception Raised.

Aferição de Erro

Quando uma afirmação é falhada, um Erro de Afirmação é levantado.

P>Vamos tomar um exemplo para compreender o erro de afirmação. Digamos que tem duas variáveis a e b, que precisa de comparar. Para verificar se a e b são iguais ou não, aplica uma assert palavra-chave antes disso, o que levantará uma Assertion excepção, quando a expressão retornará falsa.

try: a = 100 b = "DataCamp" assert a == bexcept AssertionError: print ("Assertion Exception Raised.")else: print ("Success, no error!")
Assertion Exception Raised.

Attribute Error

Quando um atributo inexistente é referenciado, e quando a referência ou atribuição desse atributo falha, é levantado um erro de atributo.

No exemplo abaixo, pode observar que o Attributes class object não tem atributo com o nome attribute.

class Attributes(object): a = 2 print (a)try: object = Attributes() print (object.attribute)except AttributeError: print ("Attribute Exception Raised.")
2Attribute Exception Raised.

Import Error

ImportError é levantado quando se tenta importar um módulo que não existe (incapaz de carregar) no seu caminho padrão ou mesmo quando se faz uma gralha no nome do módulo.

import nibabel
---------------------------------------------------------------------------ModuleNotFoundError Traceback (most recent call last)<ipython-input-6-9e567e3ae964> in <module>----> 1 import nibabelModuleNotFoundError: No module named 'nibabel'

Encerrar Erro

Encerrar Erro funciona como uma classe base para as excepções que ocorrem quando uma chave ou índice utilizado num mapeamento ou sequência de uma lista/dicionário é inválido ou não existe.

Os dois tipos de excepções levantados são:

  • IndexError
  • KeyError

Key Error

Se uma chave que está a tentar aceder não for encontrada no dicionário, é levantada uma key excepção de erro.

try: a = {1:'a', 2:'b', 3:'c'} print (a) except LookupError: print ("Key Error Exception Raised.")else: print ("Success, no error!")
Key Error Exception Raised.

Erro de índice

Quando está a tentar aceder a um índice (sequência) de uma lista que não existe nessa lista ou que está fora do alcance dessa lista, é levantado um erro de índice.

try: a = print (a) except LookupError: print ("Index Error Exception Raised, list index out of range")else: print ("Success, no error!")
Index Error Exception Raised, list index out of range

Memory Error

Como discutido anteriormente, o Erro de Memória é levantado quando uma operação não obtém memória suficiente para processar mais.

Name Error

Name Error é levantado quando um nome local ou global não é encontrado.

No exemplo abaixo, ans variável não é definida. Assim, obterá um name error.

try: print (ans)except NameError: print ("NameError: name 'ans' is not defined")else: print ("Success, no error!")
NameError: name 'ans' is not defined

Erro de Tempo de Execução

Não Aplicado Erro

Esta secção do tutorial é derivada desta Fonte. Runtime Error actua como uma classe base para o Erro Não Implementado. Os métodos abstractos em classes definidas pelo utilizador devem levantar esta excepção quando as classes derivadas substituem o método.

class BaseClass(object): """Defines the interface""" def __init__(self): super(BaseClass, self).__init__() def do_something(self): """The interface, not implemented""" raise NotImplementedError(self.__class__.__name__ + '.do_something')class SubClass(BaseClass): """Implementes the interface""" def do_something(self): """really does something""" print (self.__class__.__name__ + ' doing something!')SubClass().do_something()BaseClass().do_something()
SubClass doing something!---------------------------------------------------------------------------NotImplementedError Traceback (most recent call last)<ipython-input-1-57792b6bc7e4> in <module> 14 15 SubClass().do_something()---> 16 BaseClass().do_something()<ipython-input-1-57792b6bc7e4> in do_something(self) 5 def do_something(self): 6 """The interface, not implemented"""----> 7 raise NotImplementedError(self.__class__.__name__ + '.do_something') 8 9 class SubClass(BaseClass):NotImplementedError: BaseClass.do_something

Type Error

Type Error Exception é levantada quando dois tipos diferentes ou não relacionados de operandos ou objectos são combinados.

No exemplo abaixo, são adicionados um inteiro e uma string, o que resulta num type erro.

try: a = 5 b = "DataCamp" c = a + bexcept TypeError: print ('TypeError Exception Raised')else: print ('Success, no error!')
TypeError Exception Raised

Erro de Valor

Erro de Valor é levantado quando a operação incorporada ou uma função recebe um argumento que tem um correcto type mas inválido .

No exemplo abaixo, a operação incorporada float recebe um argumento, que é uma sequência de caracteres (valor), que é inválido para um tipo de flutuador.

try: print (float('DataCamp'))except ValueError: print ('ValueError: could not convert string to float: \'DataCamp\'')else: print ('Success, no error!')
ValueError: could not convert string to float: 'DataCamp'

Python Custom Exceptions

Esta secção do tutorial é derivada desta Fonte.

Como estudado na secção anterior do tutorial, Python tem muitas excepções incorporadas que pode utilizar no seu programa. Ainda assim, por vezes, poderá ter de criar excepções personalizadas com mensagens personalizadas para servir o seu propósito.

Pode conseguir isto criando uma nova classe, que será derivada da classe de excepção pré-definida em Python.

class UnAcceptedValueError(Exception): def __init__(self, data): self.data = data def __str__(self): return repr(self.data)Total_Marks = int(input("Enter Total Marks Scored: "))try: Num_of_Sections = int(input("Enter Num of Sections: ")) if(Num_of_Sections < 1): raise UnAcceptedValueError("Number of Sections can't be less than 1")except UnAcceptedValueError as e: print ("Received error:", e.data)
Enter Total Marks Scored: 10Enter Num of Sections: 0Received error: Number of Sections can't be less than 1

No exemplo acima, pois observou que se introduzir algo inferior a 1, será levantada e tratada uma excepção personalizada. Muitos módulos padrão definem as suas excepções para comunicar erros que podem ocorrer em funções que definem.

Deméritos de Tratamento de Excepções Python

O uso de tratamento de excepções Python tem também um efeito secundário. Tal como, programas que fazem uso de blocos try-except para lidar com excepções correrão um pouco mais lentamente, e o tamanho do seu código aumentará.

Below é um exemplo onde o módulo timeit de Python está a ser usado para verificar o tempo de execução de 2 afirmações diferentes. Em stmt1, a instrução try-except é utilizada para tratar ZeroDivisionError, enquanto em stmt2if a instrução é utilizada como condição normal de verificação. Depois, executa-se estas afirmações 10000 vezes com a variável a=0. O ponto a observar aqui é que o tempo de execução de ambas as afirmações é diferente. Verificará que stmt1, que trata da excepção, levou um tempo ligeiramente mais longo do que stmt2, que é apenas verificar o valor e não fazer nada se a condição não for satisfeita.

Hence, deve limitar o uso do tratamento de excepção Python e utilizá-lo apenas para casos raros. Por exemplo, quando não tiver a certeza se o input será um inteiro ou um flutuador para cálculos aritméticos ou quando não tiver a certeza da existência de um ficheiro enquanto tenta abri-lo.

import timeitsetup="a=0"stmt1 = '''\try: b=10/aexcept ZeroDivisionError: pass'''
stmt2 = '''\if a!=0: b=10/a'''
print("time=",timeit.timeit(stmt1,setup,number=10000))print("time=",timeit.timeit(stmt2,setup,number=10000))
time= 0.003897680000136461time= 0.0002797570000439009

Congratulações pela conclusão deste tutorial.

Como aprendeu, a manipulação excepcional ajuda a quebrar o típico fluxo de controlo do seu programa, fornecendo um mecanismo para desacoplar a manipulação de erros Python e torna o seu código mais robusto.

A manipulação excepcional Python é um dos principais factores para tornar o seu código pronto para produção e prova futura, além de adicionar testes unitários e programação orientada a objectos.

É uma técnica poderosa e é um conceito de meros quatro blocos. try bloco procura as excepções lançadas pelo código, enquanto o except bloco trata dessas excepções (incorporado e personalizado).

Um bom exercício para todos seria utilizar os quatro componentes de manipulação excepcional e tentar tornar o seu código mais robusto.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *