Skip to content

6. As exceções

Vamos agora nos concentrar nas exceções.

  

Programa (exceptions_01)

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


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

# ocorre um erro
x=4/0

Provocamos deliberadamente um erro para observar as informações geradas pelo interpretador. O resultado na tela é 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 nos fornece:

  • o tipo da exceção: ZeroDivisionError;
  • a mensagem de erro associada: divisão de inteiros ou módulo por zero. Ela está em inglês. Isso é algo que talvez queiramos alterar.

A regra fundamental na programação é que devemos fazer de tudo para evitar falhas “inesperadas”, como a mencionada acima. Mesmo em caso de erro, o programa deve ser encerrado de forma adequada, 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 exceções do tipo MyException ou derivadas. Quando ocorre uma exceção em um try, o interpretador examina as cláusulas except associadas ao **try na ordem em que foram escritas. Ele para na primeira cláusula except capaz de tratar a exceção que ocorreu. Se não encontrar nenhuma, a exceção é repassada para o método chamador. Se este tiver um bloco** **try/except**, a exceção é novamente tratada; caso contrário, ela continua subindo pela cadeia de métodos chamados. Em última instância, ela chega ao interpretador Python. Este, então, interrompe 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 interceptar todas as exceções que possam ser propagadas pelos métodos chamados.

Uma exceção traz 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 à exceção.

A sintaxe


except MyException, erreur:

atribui à variável erreur a mensagem de erro relacionada à 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 interceptação de exceções.

Esses conceitos são ilustrados pelo script a seguir.


Programa (exceptions_02)

O script a seguir lida explicitamente com os erros:


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

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

# repetimos o processo
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:
    # essa 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:
    # essa 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 em uma 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 suas 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
# 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 ocorrida, x não recebeu nenhum valor e, portanto, não sofreu alteração;
  • linhas 15-19: repetimos 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: são definidas várias cláusulas except para lidar com diversos tipos de exceção. Apenas uma cláusula except será executada, ou nenhuma, se a exceção não atender a nenhuma cláusula “except”;
  • linhas 51-55: a cláusula except pode não ter nenhum argumento. Nesse caso, ela 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 maneira de lançar uma exceção;
  • linhas 79-84: ilustram o uso de uma classe de exceção proprietária MyError;
  • linhas 79-80: a classe MyError limita-se a derivar da classe base Exception. Ela 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 qualquer tipo de objeto, e não apenas objetos derivados da classe Exception;
  • linhas 109-127: ilustram o uso da cláusula finally.

Os resultados na tela 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 lida com exceções — elas são repassadas automaticamente
    return f2(x)

def f2(y):
    # não se trata das exceções — elas são repassadas 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 sobem pela cadeia de métodos chamados
# até que um método as intercepte. Aqui, 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 ele reenvia
def f4(x):
    try:
        return f5(x)
    except MyError as infos:
        # enriquecemos a exceção e, em seguida, a reenviamos
        raise MyError(infos,"exception dans f4")

def f5(y):
    try:
        return f6(y)
    except MyError as infos:
        # enriquecemos a exceção e, em seguida, a relançamos
        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:

  • linhas 27-30, na chamada main --> f1 --> f2 --> f3 (linha 28), a exceção MyError lançada por f3 será propagada até main. Ela 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 será propagada até main. Ela 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 na tela 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')