Skip to content

6. Exceções

Vamos agora analisar as exceções.

  

Programa (exceptions_01)

O primeiro script ilustra a necessidade de lidar com exceções.

1
2
3
4
#   -*- coding=utf-8 -*-

#  we cause an error
x=4/0

Provocamos intencionalmente um erro para ver as informações produzidas pelo interpretador. A saída é a seguinte:

1
2
3
4
Traceback (most recent call last):
  File "D:\data\istia-1112\python\tutoriel\exceptions_01.py", line 4, in <module>
    x=4/0
ZeroDivisionError: integer division or modulo by zero

A linha 4 dá-nos:

  • o tipo de exceção: ZeroDivisionError;
  • a mensagem de erro associada: divisão ou módulo inteiro por zero. Está em inglês. Isto é algo que talvez queira alterar.

A regra essencial na programação é que devemos fazer tudo o que for possível para evitar falhas «incontroladas» como a acima referida. Mesmo em caso de erro, o programa deve terminar corretamente, fornecendo informações sobre o erro que ocorreu.

A sintaxe para o tratamento de exceções é a seguinte:


try:
    actions susceptibles de lancer une exception
except [classe d'exception, ...]:
    actions de gestion de l'exception
finally:
   actions toujours exécutées qu'il y ait exception ou non

No bloco try, a execução das ações é interrompida assim que ocorre uma exceção. Neste caso, a execução continua com as ações na cláusula except.

O


except [MyException, ...]:

intercepta exceções do tipo MyException ou tipos derivados. Quando ocorre uma exceção num bloco try, o interpretador examina as cláusulas except associadas ao bloco try na ordem em que foram escritas. Ele pára na primeira cláusula except capaz de tratar a exceção que ocorreu. Se não encontrar nenhuma, a exceção é propagada de volta para o método chamador. Se esse método tiver um bloco try/except, a exceção é tratada novamente; caso contrário, continua a propagar-se pela cadeia de métodos chamados. Por fim, chega ao interpretador Python. O interpretador encerra então o programa em execução e exibe uma mensagem de erro do tipo mostrado no exemplo anterior. A regra é, portanto, que o programa principal deve capturar todas as exceções que possam propagar-se a partir dos métodos chamados.

Uma exceção contém informações sobre o erro que ocorreu. Estas informações podem ser recuperadas utilizando a seguinte sintaxe:


except MyException as informations:

information é uma tupla que contém as informações relacionadas com a exceção.

A sintaxe


except MyException, erreur:

atribui a mensagem de erro associada à exceção à variável error.

Para lançar uma exceção, utilize a sintaxe


raise MyException(param1, param2, ...)

onde MyException é, na maioria das vezes, uma classe derivada da classe Exception. Os parâmetros passados ao construtor da classe estarão disponíveis para a cláusula except das estruturas de tratamento de exceções.

Estes conceitos são ilustrados pelo seguinte script.


Programa (exceptions_02)

O script a seguir trata explicitamente os erros:

#   -*- coding=utf-8 -*-

i=0
#  cause an error and manage it
x=2
try:
    x=4/0
except ZeroDivisionError, erreur:
    print ("%s : %s ") % (i, erreur)
#  the value of x has not changed
print "x=%s" % (x)

#   here we go again
i+=1
try:
    x=4/0
except Exception, erreur:
    #  we intercept the most general exception
    print ("%s : %s ") % (i, erreur)

#   several types of exceptions can be intercepted 
i+=1
try:
    x=4/0
except ValueError, erreur:
    #  this exception does not occur here
    print ("%s : %s ") % (i, erreur)
except Exception, erreur:
    #  we intercept the most general exception
    print ("%s : (Exception) %s ") % (i, erreur)
except ZeroDivisionError, erreur:
    #   we intercept a specific type
    print ("%s : (ZeroDivisionError) %s ") % (i, erreur)

#  start again, changing the order 
i+=1
try:
    x=4/0
except ValueError, erreur:
    #  this exception does not occur here
    print ("%s : %s ") % (i, erreur)
except ZeroDivisionError, erreur:
    #   we intercept a specific type
    print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
except Exception, erreur:
    #  we intercept the most general exception
    print ("%s : (Exception) %s ") % (i, erreur)

#  an except clause with no arguments
i+=1
try:
    x=4/0
except:
    #  we're not interested in the nature of the exception or the error msg
    print ("%s : il y a eu un probleme ") % (i)

#   another type of exception
i+=1
try:
    x=int("x")
except ValueError, erreur:
    print ("%s : %s ") % (i, erreur)

#  an exception transports information in a tuple accessible to the program
i+=1
try:
    x=int("x")
except ValueError as infos:
    print ("%s : %s ") % (i, infos)

#   exceptions can be thrown
i+=1
try:
    raise ValueError("param1","param2","param3")
except ValueError as infos:
    print ("%s : %s ") % (i, infos)

#   you can create your own exceptions
class MyError(Exception):
    pass

#   throw MyError exception
i+=1
try:
    raise MyError("info1","info2", "info3")
except MyError as infos:
    print ("%s : %s ") % (i, infos)

#   throw MyError exception
i+=1
try:
    raise MyError("mon msg d'erreur")
except MyError, erreur:
    print ("%s : %s ") % (i, erreur)

#   any type of object can be launched
class Objet:
    def __init__(self,msg):
        self.msg=msg
    def __str__(self):
        return self.msg

i+=1
try:
    raise Objet("pb...")
except Objet as erreur:
    print "%s : %s" % (i, erreur)

#  the finally clause is always executed
#  whether or not there is an exception
i+=1
x=None
try:
    x=1
except:
    print "%s : exception" % (i)
finally:
    print "%s : finally x=%s" % (i,x)

i+=1
x=None
try:
    x=2/0
except:
    print "%s : exception" % (i)
finally:
    print "%s : finally x=%s" % (i,x)

Notas:

  • linhas 6–9: tratam uma divisão por zero;
  • linha 8: capturamos a exceção exata que ocorre;
  • linha 11: devido à exceção que ocorreu, x não recebeu um valor e, portanto, não se alterou;
  • linhas 15-19: fazemos o mesmo, mas capturamos uma exceção de nível superior do tipo Exception. Uma vez que a exceção ZeroDivisionError deriva da classe Exception, a cláusula except irá capturá-la;
  • linhas 23–33: incluímos várias cláusulas except para lidar com vários tipos de exceção. Apenas uma cláusula except será executada, ou nenhuma, se a exceção não corresponder a nenhuma cláusula except;
  • linhas 51–55: A cláusula except pode não ter argumentos. Neste caso, captura todas as exceções;
  • linhas 59–62: introduzimos a exceção ValueError;
  • linhas 66–69: recuperamos a informação contida na exceção;
  • linhas 73–76: apresentam como lançar uma exceção;
  • linhas 79–84: ilustram a utilização de uma classe de exceção personalizada, MyError;
  • linhas 79–80: a classe MyError simplesmente herda da classe base Exception. Não acrescenta nada à sua classe base. Mas agora, pode ser explicitamente nomeada em cláusulas except;
  • linhas 97–107: demonstram que, em Python, é possível lançar qualquer tipo de objeto, não apenas objetos derivados da classe Exception;
  • Linhas 109–127: demonstram o uso da cláusula finally.

A saída no ecrã é a seguinte:

0 : integer division or modulo by zero
x=2
1 : integer division or modulo by zero
2 : (Exception) integer division or modulo by zero
3 : (ZeroDivisionError) integer division or modulo by zero
4 : il y a eu un probleme
5 : invalid literal for int() with base 10: 'x'
6 : invalid literal for int() with base 10: 'x'
7 : ('param1', 'param2', 'param3')
8 : ('info1', 'info2', 'info3')
9 : mon msg d'erreur
10 : pb...
11 : finally x=1
12 : exception
12 : finally x=None

Programa (exceptions_03)

Este novo script ilustra como as exceções são propagadas ao longo da cadeia de métodos de chamada:

#    -*- coding=utf-8 -*-

#    a proprietary exception
class MyError(Exception):
    pass

#    three methods
def f1(x):
    #  exceptions are not handled - they are automatically logged
    return f2(x)

def f2(y):
    #  exceptions are not handled - they are automatically logged
    return f3(y)

def f3(z):
    if (z % 2) == 0:
        #  if z is even, throw an exception 
        raise MyError("exception dans f3")
    else:
        return 2*z

#   ---------- hand

#  exceptions go up the chain of methods called
#  until a method intercepts it. In this case, it will be main
try:
    print f1(4)
except MyError as infos:
    print "type :%s, arguments : %s" % (type(infos),infos)

#  a method can enrich the exceptions it raises
def f4(x):
    try:
        return f5(x)
    except MyError as infos:
        #  we enrich the exception then relaunch it
        raise MyError(infos,"exception dans f4")

def f5(y):
    try:
        return f6(y)
    except MyError as infos:
        #  we enrich the exception then relaunch it
        raise MyError(infos, "exception dans f5")

def f6(z):
    if (z % 2) == 0:
        #    throw an exception
        raise MyError("exception dans f6")
    else:
        return 2*z

#   ---------- hand
try:
    print f4(4)
except MyError as infos:
    print "type :%s, arguments : %s" % (type(infos),infos)

Notas:

  • Linhas 27–30: Na chamada main --> f1 --> f2 --> f3 (linha 28), a exceção MyError lançada por f3 propagar-se-á até main. Será então tratada pela cláusula except na linha 29;
  • linhas 55–58: na chamada main → f4 → f5 → f6 (linha 56), a exceção MyError lançada por f6 propagar-se-á até main. Será então tratada pela cláusula except na linha 29. Desta vez, à medida que se propaga pela cadeia de métodos de chamada, a exceção MyError é enriquecida com informações adicionadas por cada método ao longo do caminho.

A saída no ecrã é a seguinte:

type :<class '__main__.MyError'>, arguments : exception dans f3
type :<class '__main__.MyError'>, arguments : (MyError(MyError('exception dans f6',), 'exception dans f5'), 'exception dans f4')