Skip to content

6. As exceções

Vamos agora abordar as exceções.

  

Programa (exceptions_01)

O primeiro script ilustra a necessidade de gerir as exceções.


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

# provoca-se um erro
x=4/0

Provocamos deliberadamente um erro para ver as informações geradas pelo interpretador. O resultado no ecrã é o 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 indica-nos:

  • o tipo da exceção: ZeroDivisionError;
  • a mensagem de erro associada: «integer division or modulo by zero». Está em inglês. É algo que se pode querer alterar.

A regra fundamental na programação é que devemos fazer tudo o que for possível para evitar falhas «inesperadas» como a acima referida. Mesmo em caso de erro, o programa deve terminar de forma correta, 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. Nesse caso, a execução prossegue com as ações da cláusula «except».

A cláusula


except [MyException, ...]:

intercepta as exceções do tipo MyException ou derivadas. Quando ocorre uma exceção num «try», o interpretador examina as cláusulas «except» associadas ao «try» na ordem em que foram escritas. Detém-se na primeira cláusula «except» que permite tratar a exceção que ocorreu. Se não encontrar nenhuma, a exceção é reenviada para o método chamador. Se este tiver um bloco **try/except**, a exceção é novamente tratada; caso contrário, continua a subir pela cadeia de métodos chamados. Em última instância, chega ao interpretador Python. Este interrompe então o programa em execução e exibe uma mensagem de erro do tipo apresentado no exemplo anterior. A regra é, portanto, que o programa principal deve interceptar todas as exceções que possam ser propagadas a partir dos métodos chamados.

Uma exceção transporta consigo informações sobre o erro que ocorreu. É possível obtê-las com a seguinte sintaxe:


except MyException as informations:

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

A sintaxe


except MyException, erreur:

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

Para lançar uma exceção, utiliza-se a sintaxe


raise MyException(param1, param2, ...)

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

Estes conceitos são ilustrados pelo seguinte script.


Programa (exceptions_02)

O script seguinte gere explicitamente os erros:


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

i=0
# provoca-se um erro e este é tratado
x=2
try:
    x=4/0
except ZeroDivisionError, erreur:
    print ("%s : %s ") % (i, erreur)
# o valor de x não se alterou
print "x=%s" % (x)

# recomeçamos
i+=1
try:
    x=4/0
except Exception, erreur:
    # intercepta-se a exceção mais geral
    print ("%s : %s ") % (i, erreur)

# é possível interceptar vários tipos de exceções 
i+=1
try:
    x=4/0
except ValueError, erreur:
    # esta exceção não ocorre aqui
    print ("%s : %s ") % (i, erreur)
except Exception, erreur:
    # interceptamos a exceção mais geral
    print ("%s : (Exception) %s ") % (i, erreur)
except ZeroDivisionError, erreur:
    # intercepta-se um tipo específico
    print ("%s : (ZeroDivisionError) %s ") % (i, erreur)

# repetimos o processo alterando a ordem 
i+=1
try:
    x=4/0
except ValueError, erreur:
    # esta exceção não ocorre aqui
    print ("%s : %s ") % (i, erreur)
except ZeroDivisionError, erreur:
    # intercepta-se um tipo específico
    print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
except Exception, erreur:
    # intercepta-se a exceção mais geral
    print ("%s : (Exception) %s ") % (i, erreur)

# uma cláusula «except» sem argumentos
i+=1
try:
    x=4/0
except:
    # não nos interessamos pela natureza da exceção nem pela mensagem de erro
    print ("%s : il y a eu un probleme ") % (i)

# outro tipo de exceção
i+=1
try:
    x=int("x")
except ValueError, erreur:
    print ("%s : %s ") % (i, erreur)

# uma exceção transporta informações numa tupla acessível ao programa
i+=1
try:
    x=int("x")
except ValueError as infos:
    print ("%s : %s ") % (i, infos)

# é possível lançar exceções
i+=1
try:
    raise ValueError("param1","param2","param3")
except ValueError as infos:
    print ("%s : %s ") % (i, infos)

# é possível criar as próprias exceções
class MyError(Exception):
    pass

# lança-se a exceção MyError
i+=1
try:
    raise MyError("info1","info2", "info3")
except MyError as infos:
    print ("%s : %s ") % (i, infos)

# lança-se a exceção MyError
i+=1
try:
    raise MyError("mon msg d'erreur")
except MyError, erreur:
    print ("%s : %s ") % (i, erreur)

# é possível lançar qualquer tipo de objeto
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)

# a cláusula finally é sempre executada
# quer haja ou não uma exceção
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: trata-se de uma divisão por zero;
  • linha 8: intercepta-se a exceção exata que ocorre;
  • linha 11: devido à exceção que ocorreu, x não recebeu qualquer valor e, por isso, não sofreu qualquer alteração;
  • linhas 15-19: repete-se o mesmo procedimento, mas interceptando uma exceção de nível superior do tipo Exception. Como a exceção ZeroDivisionError deriva da classe Exception, a cláusula except irá interrompê-la;
  • linhas 23-33: definem-se várias cláusulas except para gerir vários tipos de exceção. Será executada apenas uma cláusula except 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 nenhum argumento. Nesse caso, interrompe todas as exceções;
  • linhas 59-62: introduzem a exceção ValueError;
  • linhas 66-69: recuperam-se as informações transportadas pela exceção;
  • linhas 73-76: apresentam a forma de lançar uma exceção;
  • linhas 79-84: ilustram a utilização de uma classe de exceção proprietária MyError;
  • linhas 79-80: a classe MyError limita-se a derivar da classe base Exception. Não acrescenta nada à sua classe base. Mas agora pode ser nomeada explicitamente nas cláusulas «except»;
  • linhas 97-107: ilustram que, em Python, é possível lançar, de facto, qualquer tipo de objeto e não apenas objetos derivados da classe Exception;
  • linhas 109-127: ilustram a utilização da cláusula finally.

Os resultados no ecrã são os seguintes:

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 a propagação de exceções na cadeia de métodos chamadores:


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

# uma exceção proprietária
class MyError(Exception):
    pass

# três métodos
def f1(x):
    # não se gerem as exceções — estas são propagadas automaticamente
    return f2(x)

def f2(y):
    # não se tratam as exceções — estas são propagadas automaticamente
    return f3(y)

def f3(z):
    if (z % 2) == 0:
        # se z for par, lança-se uma exceção 
        raise MyError("exception dans f3")
    else:
        return 2*z

#---------- main

# as exceções são propagadas pela cadeia de métodos chamados
# até que um método as intercepte. Neste caso, será o método «main»
try:
    print f1(4)
except MyError as infos:
    print "type :%s, arguments : %s" % (type(infos),infos)

# um método pode enriquecer as exceções que reenvia
def f4(x):
    try:
        return f5(x)
    except MyError as infos:
        # enriquece-se a exceção e, em seguida, reenvia-se
        raise MyError(infos,"exception dans f4")

def f5(y):
    try:
        return f6(y)
    except MyError as infos:
        # enriquecemos a exceção e, em seguida, relançamo-la
        raise MyError(infos, "exception dans f5")

def f6(z):
    if (z % 2) == 0:
        # lança-se uma exceção
        raise MyError("exception dans f6")
    else:
        return 2*z

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

Notas:

  • nas linhas 27-30, na chamada main --> f1 --> f2 --> f3 (linha 28), a exceção MyError lançada por f3 será propagada até main. Será então tratada pela cláusula except da linha 29;
  • linhas 55-58: na chamada main --> f4 --> f5 --> f6 (linha 56), a exceção lançada MyError por f6 irá propagar-se até main. Será então tratada pela cláusula except da linha 29. Desta vez, ao subir pela cadeia de métodos chamadores, a exceção MyError é enriquecida com informações adicionadas por cada método percorrido.

Os resultados no ecrã são os seguintes:

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