Executando verificação de segurança...
0
  1. Introdução à Metaprogramação
  2. Reflexão
  3. Anotações
  4. Decoradores
  5. Geração de Código
  6. Orientação a Aspectos
  7. Sistemas de Metaprogramação
  8. Usando Metaprogramação de Forma Eficiente
  9. Conclusão




4 Decoradores

Os decoradores são uma técnica avançada da programação que permite adicionar comportamento a um método ou classe existente de forma dinâmica, sem precisar modificar o código fonte do método ou classe original.

Neste tópico, vamos explorar como os decoradores funcionam e como usá-los em suas aplicações.


4.1 Como Funcionam os Decoradores

Os decoradores são implementados como funções que recebem uma função ou classe como argumento e retornam uma nova versão modificada da função ou classe original.
Eles são especialmente úteis quando você quer adicionar funcionalidade a uma classe ou método sem ter que herdar de uma nova classe ou sobrescrever o método existente.

Por exemplo, aqui está como podemos implementar um decorador que adiciona um log de depuração a um método:

def debug(func):
    def wrapper(*args, **kwargs):
        print(f"Chamando {func.__name__} com os argumentos {args} {kwargs}")
        result = func(*args, **kwargs)
        print(f"Resultado: {result}")
        return result
    return wrapper

Neste exemplo, estamos definindo uma função debug que é um decorador.
Ela recebe uma função func como argumento e retorna uma nova função wrapper que é uma versão modificada da função original.
Quando a função add é chamada, o decorador debug é aplicado a ela e a função wrapper é chamada em vez da função original.
A função wrapper adiciona o log de depuração antes e depois de chamar a função original e retorna o resultado da chamada original.

Dessa forma, os decoradores nos permitem adicionar comportamento a um método ou classe existente de forma dinâmica e flexível, sem precisar modificar o código fonte original.


4.2 Como Usar Decoradores

Para usar um decorador, basta anexá-lo ao método ou classe que você deseja modificar.
O decorador será aplicado automaticamente quando o método ou classe for chamado.
Por exemplo, aqui está como podemos usar o decorador debug que definimos anteriormente:

@debug
def add(x, y):
    return x + y

print(add(2, 3))

Neste exemplo, estamos anexando o decorador debug ao método add.
Quando o método add é chamado, o decorador debug é aplicado automaticamente e a função wrapper é chamada em vez da função original.
Isso adiciona o log de depuração ao método add e retorna o resultado da chamada original.

Você também pode passar argumentos para o decorador quando o anexa ao método ou classe.
Por exemplo, aqui está como podemos modificar o decorador debug para aceitar um argumento prefix:

def debug(prefix):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"{prefix}: Chamando {func.__name__} com os argumentos {args} {kwargs}")
            result = func(*args, **kwargs)
            print(f"{prefix}: Resultado: {result}")
            return result
        return wrapper
    return decorator

@debug("DEBUG")
def add(x, y):
    return x + y

print(add(2, 3))

Neste exemplo, estamos definindo o decorador debug para receber um argumento prefix.
Quando o decorador é anexado ao método add, passamos o valor "DEBUG" para o argumento prefix. Isso adiciona o prefixo "DEBUG: " ao log de depuração adicionado pelo decorador.

Dessa forma, os decoradores nos permitem adicionar comportamento a um método ou classe de forma flexível e personalizada, dependendo de nossas necessidades específicas.


4.3 Exemplos de Decoradores

Existem muitas bibliotecas e frameworks que usam decoradores de forma eficiente para adicionar funcionalidades aos métodos e classes.
Aqui estão alguns exemplos de uso comum dos decoradores:

  • Pytest: A biblioteca de teste Pytest permite que você anexe decoradores aos métodos de teste para modificar o comportamento deles. Por exemplo, você pode usar o decorador @pytest.mark.xfail para marcar um teste como esperado para falhar ou o decorador @pytest.mark.parametrize para rodar o mesmo teste com diferentes entrada e argumentos.
  • Unittest: A biblioteca de teste Unittest do Python também permite que você anexe decoradores aos métodos de teste. Por exemplo, você pode usar o decorador @unittest.skip para pular um teste ou o decorador @unittest.expectedFailure para marcar um teste como esperado para falhar.

4.3.1 Exemplo de Decoradores com Pytest

Usando o Pytest como exemplo, podemos criar um decorador para marcar uma função de teste como "skip" se ela estiver em um sistema operacional específico:

import pytest
import platform

def skip_on_windows(func):
    if platform.system() == "Windows":
        return pytest.mark.skip(reason="Test is not supported on Windows")(func)
    return func

@skip_on_windows
def test_example():
    # Test code goes here
    assert True

Neste exemplo, estamos criando um decorador skip_on_windows que verifica o sistema operacional atual e, se for o Windows, marca a função de teste test_example como "skip" usando o marcador do Pytest pytest.mark.skip.
Se o sistema operacional não for o Windows, a função de teste é executada normalmente.


4.3.2 Exemplo de Decoradores com Unittest

Usando o Unittest como exemplo, podemos criar um decorador para marcar uma função de teste como "expected failure" se ela estiver em um sistema operacional específico:

import unittest
import platform

def expected_failure_on_windows(func):
    if platform.system() == "Windows":
        return unittest.expectedFailure(func)
    return func

@expected_failure_on_windows
def test_example():
    # Test code goes here
    assert False

Neste exemplo, estamos criando um decorador expected_failure_on_windows que verifica o sistema operacional atual e, se for o Windows, marca a função de teste test_example como "expected failure" usando o método unittest.expectedFailure.
Se o sistema operacional não for o Windows, a função de teste é executada normalmente.





🔝 (Voltar para o Início)



Carregando publicação patrocinada...