Contents

Como criar excepções personalizadas em Python

As classes de exceção incorporadas do Python não abordam determinadas situações de erro que podem surgir no seu código. Nesses casos, terá de criar exceções personalizadas para lidar com esses erros de forma eficaz.

No domínio das linguagens de programação como o Python, é possível criar classes de exceção personalizadas que podem ser levantadas durante instâncias de cenários de erro específicos. Ao utilizar estes tipos de exceção personalizados, é possível abordar eficazmente problemas mais precisos e detalhados na sua base de código, promovendo a clareza e a facilidade de manutenção em termos de organização e legibilidade do código.

Por que você precisa de exceções personalizadas?

/pt/images/own-you-error.jpg

Durante a evolução de um programa de software, podem surgir complicações imprevistas resultantes de alterações ao código-fonte, da interface com módulos ou bibliotecas adicionais ou da cooperação com aplicações remotas. A gestão eficaz destas irregularidades é essencial para garantir que o sistema pode recuperar destes contratempos ou executar procedimentos de terminação de uma forma equilibrada.

O Python oferece uma gama de classes de exceção incorporadas que cobrem erros como ValueError , TypeError , FileNotFoundError , entre outros. Embora estas excepções incorporadas sirvam bem o seu propósito, só por vezes podem representar com precisão os erros que podem ocorrer na sua aplicação.

A personalização de excepções personalizadas permite um ajuste mais preciso na abordagem de necessidades específicas numa aplicação, fornecendo informações valiosas para os programadores que possam encontrar esse código.

Como definir excepções personalizadas

Para criar excepções personalizadas, defina uma classe Python que herda da classe Exception . A classe Exception oferece a funcionalidade base de que necessitará para lidar com exceções e pode personalizá-la para adicionar funcionalidades com base nas suas necessidades específicas.

Ao desenvolver categorias de exceção personalizadas, é importante manter a simplicidade e, ao mesmo tempo, incorporar funcionalidades cruciais para preservar os detalhes do erro. Os gestores de excepções podem posteriormente utilizar estas características para tratar os erros de forma eficaz.

Aqui está uma classe de exceção personalizada, MyCustomError:

 class MyCustomError(Exception):
    def __init__(self, message=None):
        self.message = message
        super().__init__(message)

Durante o seu processo de inicialização, esta classe em particular permite a inclusão condicional de um parâmetro de mensagem. Utilizando a palavra-chave ‘super’, chama efetivamente o construtor da sua classe Exception ancestral, que serve como um componente essencial nos protocolos de tratamento de excepções.

Como criar excepções personalizadas

De facto, para criar um obstáculo e gerar uma instância da nossa categoria de exceção personalizada, pode empregar a palavra-chave “raise” em conjunto com um exemplo do seu tipo de exceção personalizado, fornecendo-lhe uma mensagem que encapsule a natureza do problema:

 if True:
    raise MyCustomError("A Custom Error Was Raised!!!.")

Também se pode optar por criar uma exceção sem fornecer quaisquer argumentos,

 if True:
    raise MyCustomError # shorthand

Ambos os formatos são igualmente apropriados para criar mensagens de erro personalizadas.

/pt/images/raised-custom-error.jpg

Como lidar com excepções personalizadas

Ao trabalhar com excepções personalizadas, é essencial empregar uma estratégia semelhante à utilizada para lidar com excepções padrão. Isso envolve a utilização de blocos try-except-finally para capturar essas circunstâncias excepcionais e executar as medidas corretivas adequadas. Ao fazê-lo, podemos gerir e recuperar eficazmente de ocorrências inesperadas que possam surgir durante a execução do nosso código.

 try:
    print("Hello, You're learning how to All Things N Custom Errors")
    raise MyCustomError("Opps, Something Went Wrong!!!.")
except MyCustomError as err:
    print(f"Error: {err}")
finally:
    print("Done Handling Custom Error")

Desta forma, estamos equipados para lidar com todas as variedades concebíveis de excepções excepcionais que possam surgir.

/pt/images/handling-custom-error.jpg

Caso surja uma exceção durante a execução de um bloco try, o bloco except correspondente pode capturá-la e geri-la. No entanto, se não existir um bloco except adequado para lidar com a exceção, então qualquer bloco finally presente será executado, levando a que a exceção seja levantada mais uma vez. O principal objetivo da utilização de um bloco finally deve ser a realização de operações de limpeza obrigatórias, independentemente de ocorrer ou não uma exceção.

 try:
    raise KeyboardInterrupt
except MyCustomError as err:
    print(f"Error: {err}")
finally:
    print("Did not Handle the KeyboardInterrupt Error. \
Can Only Handle MyCustomError")

Neste caso, ocorre uma interrupção do teclado, no entanto, a secção “except” envolvente limita-se a lidar com instâncias de “MyCustomError”. Consequentemente, quando a cláusula “finally” é executada, a exceção não reconhecida anteriormente é reavivada.

/pt/images/re-raise-unhandled-exception.jpg

Herdando classes de erro personalizadas

O princípio da Programação Orientada a Objetos (OOP) permite estender a herança não apenas a tipos de dados padrão, mas também a categorias de exceção personalizadas, da mesma forma que em classes típicas. Através desta técnica, é possível estabelecer classes de exceção que oferecem maior clareza relativamente às circunstâncias por detrás de um erro. Essa metodologia permite lidar com erros em várias fases do programa e resulta em uma maior compreensão da causa do problema.

Para garantir o tratamento consistente de vários cenários de erro numa integração de API externa numa aplicação Web, é aconselhável estabelecer uma abordagem normalizada, criando uma classe de exceção específica conhecida como BaseAPIException.

 class BaseAPIException(Exception):
    """Base class for API-related exceptions."""
    def __init__(self, message):
        super().__init__(message)
        self.message = message

Para ampliar a funcionalidade de uma classe de exceção personalizada, é possível criar subclasses derivadas da classe principal. Este processo permite a criação de excepções especializadas que partilham atributos e comportamentos comuns com a sua classe-mãe, possuindo também características únicas adaptadas a situações específicas.

 class APINotFoundError(BaseAPIException):
    """Raised when the requested resource is not found in the API."""
    pass

class APIAuthenticationError(BaseAPIException):
    """Raised when there's an issue with authentication to the API."""
    pass

class APIRateLimitExceeded(BaseAPIException):
    """Raised when the rate limit for API requests is exceeded."""
    pass

Ao implementar a sua aplicação Web, é essencial tratar quaisquer potenciais erros que possam surgir durante as chamadas à API, criando e capturando excepções personalizadas específicas. Ao fazê-lo, pode gerir eficazmente estas situações com uma lógica de programação bem definida e adaptada a cada situação.

 def request_api():
    try:
        # Simulate an API error for demonstration purposes
        raise APINotFoundError("Requested resource not found.")
    except APINotFoundError as err:
        # Log or handle the 'Not Found' error case
        print(f"API Not Found Error: {err}")
    except APIAuthenticationError:
        # Take appropriate actions for authentication error
        print(f"API Authentication Error: {err}")
    except APIRateLimitExceeded:
        # Handle the rate limit exceeded scenario
        print(f"API Rate Limit Exceeded: {err}")
    except BaseAPIException:
        # Handle other unknown API exceptions
        print(f"Unknown API Exception: {err}") 

A cláusula final except é responsável pela captura de quaisquer excepções que possam surgir na classe-mãe, funcionando como um manipulador de erros abrangente para quaisquer problemas imprevistos relacionados com a interface de programação de aplicações (API).

/pt/images/inheriting-custom-exception.jpg

Quando alguém herda classes de exceção personalizadas, é capaz de gerir eficientemente os erros numa API. Esta metodologia permite separar o tratamento de erros das complexidades da implementação da API, facilitando assim a adição de excepções únicas ou alterações à medida que a API progride ou enfrenta novos cenários de erro.

Envolvimento de excepções personalizadas

O envolvimento de excepções envolve a captura de uma exceção, envolvendo-a numa exceção personalizada e levantando esta nova exceção juntamente com a identificação da exceção original como a sua razão subjacente. Esta abordagem serve para fornecer contexto para mensagens de erro e esconde particularidades de implementação do código de invocação.

No caso de uma aplicação Web comunicar com uma API e encontrar um LookupError , é possível capturar este erro através do tratamento de excepções. Subsequentemente, é possível gerar uma exceção personalizada APINotFoundError que funciona como uma classe derivada da classe original Exception em Python. Esta nova exceção pode incluir a exceção inicial LookupError como o seu fator causal subjacente, fornecendo assim um contexto adicional relativamente ao problema específico em questão.

 def request_api():
    try:
        # Simulate an API error for demonstration purposes
        # Assuming the external API raised a LookupError
        raise LookupError("Sorry, You Encountered A LookUpError !!!")
    except LookupError as original_exception:
        try:
            # Wrap the original exception with a custom exception
            raise APINotFoundError \
                 ("Requested resource not found.") from original_exception
        except APINotFoundError as wrapped_exception:
            # Handle the wrapped exception here
             print(f"Caught wrapped API exception: {wrapped_exception}")

            # or re-raise it if necessary
            raise

try:
    request_api()
except APINotFoundError as err:
    print(f"Caught API exception: {err.__cause__}")

Utilize uma cláusula “from” em conjunto com a instrução “raise” para aludir à exceção inicial na sua exceção adaptada.

/pt/images/wrapping-custom-exception.jpg

A exceção personalizada incorpora a exceção original como a sua causa subjacente, dotando-a de uma relação causal que pode ser rastreada até à origem do problema. Ao examinar este atributo, é possível discernir as origens de quaisquer discrepâncias ou anomalias encontradas durante a execução em tempo de execução.

O envelopamento de excepções permite o fornecimento de um contexto mais substantivo, ao mesmo tempo que transmite notificações de erro mais relevantes aos utilizadores finais, sem revelar o funcionamento intrincado do código ou da API. Além disso, facilita a organização e a resolução de diferentes tipos de erros de uma forma ordenada e consistente.

Personalizando o comportamento de classes em Python

Por meio da herança da classe de exceção básica fornecida pelo Python, é possível desenvolver exceções básicas, mas eficazes, a serem levantadas durante a ocorrência de falhas específicas em seu código. Além disso, através da implementação de métodos mágicos ou “dunder”, podem ser estabelecidos comportamentos personalizados para as classes de exceção.