Menu
Coddy logo textTech

Decoradores de Classe

Parte da seção Object Oriented Programming do Journey de Python da Coddy. Lição 40 de 64.

Decoradores de classe permitem que você modifique ou aprimore classes ao envolvê-las com outra função. Eles funcionam como decoradores de função, mas são aplicados a classes inteiras.

Aqui está uma classe simples sem decoração:

class Person:
    def __init__(self, name):
        self.name = name
        
    def greet(self):
        return f"Hello, my name is {self.name}"

Crie um decorador de classe que adiciona um novo método:

def add_farewell(cls):
    def farewell(self):
        return f"Goodbye from {self.name}"
    
    cls.farewell = farewell  # Adiciona o método à classe
    return cls

Aplique o decorador a uma classe usando @:

@add_farewell
class EnhancedPerson:
    def __init__(self, name):
        self.name = name
        
    def greet(self):
        return f"Hello, my name is {self.name}"

Agora a classe possui tanto os métodos originais quanto os adicionados:

person = EnhancedPerson("Alice")
print(person.greet())     # Olá, meu nome é Alice
print(person.farewell())  # Adeus de Alice

Você também pode envolver um método existente — armazenando o original para que você ainda possa chamá-lo dentro do wrapper. Isso é útil quando você deseja adicionar comportamento (como rastreamento ou log) em torno de um método que a classe já possui:

def add_tracking(cls):
    original_greet = cls.greet  # Armazena o método original

    def tracked_greet(self):
        print(f"greet was called")   # Comportamento extra antes
        return original_greet(self)  # Chama o método original

    cls.greet = tracked_greet  # Substitui o método na classe
    return cls

@add_tracking
class TrackedPerson:
    def __init__(self, name):
        self.name = name

    def greet(self):
        return f"Hello, my name is {self.name}"

Quando você chama greet agora, o wrapper executa primeiro e depois delega para o original:

person = TrackedPerson("Alice")
print(person.greet())
# greet foi chamado
# Olá, meu nome é Alice

Os principais passos para envolver (wrapping) um método existente são:
1. Salve o método original: original = cls.method
2. Defina uma nova função que chama original(self) além de qualquer lógica extra.
3. Atribua a nova função de volta à classe: cls.method = new_function
4. Retorne a classe modificada.

Ponto Chave: Decoradores de classe recebem uma classe como entrada, a modificam ou aprimoram e retornam a classe modificada. Eles podem adicionar métodos, modificar atributos ou envolver funcionalidades existentes. Use @decorator_name acima da definição da classe para aplicá-los. Isso fornece uma maneira limpa de estender a funcionalidade da classe sem herança.

challenge icon

Desafio

Médio

Neste desafio, você implementará um decorador de classe que adiciona a funcionalidade de rastreamento de chamadas de métodos.

  • decorator.py - Implemente o decorador de classe add_counter
  • calculator.py - Contém as classes que usarão seu decorador

O arquivo driver.py contém cenários de teste extensivos que validarão sua implementação em relação a vários casos de uso, padrões de herança e casos de borda. Você não precisa modificar este arquivo, mas examiná-lo ajudará você a entender o comportamento esperado.

Experimente você mesmo

from calculator import Calculator

# Manipulador abrangente de casos de teste
test_case = input()

if test_case == "basic_functionality":
    calc = Calculator()
    print(calc.add(5, 3))  # Deve imprimir 8
    print(calc.add(2, 7))  # Deve imprimir 9
    print(calc.subtract(10, 4))  # Deve imprimir 6
    print(calc.call_counts)  # Deve imprimir {'add': 2, 'subtract': 1}

elif test_case == "multiple_instances":
    calc1 = Calculator()
    calc2 = Calculator()
    
    print(calc1.add(5, 3))  # Deve imprimir 8
    print(calc2.add(2, 7))  # Deve imprimir 9
    print(calc1.subtract(10, 4))  # Deve imprimir 6
    
    # Ambas as instâncias devem compartilhar o mesmo call_counts
    print(calc1.call_counts)  # Deve imprimir {'add': 2, 'subtract': 1}
    print(calc2.call_counts)  # Deve imprimir {'add': 2, 'subtract': 1}
    print(calc1.call_counts is calc2.call_counts)  # Deve imprimir True

elif test_case == "method_call_order":
    calc = Calculator()
    
    print(calc.add(1, 2))  # Deve imprimir 3
    print(calc.subtract(5, 2))  # Deve imprimir 3
    print(calc.add(10, 20))  # Deve imprimir 30
    print(calc.add(7, 3))  # Deve imprimir 10
    print(calc.call_counts)  # Deve imprimir {'add': 3, 'subtract': 1}

elif test_case == "large_values":
    calc = Calculator()
    
    print(calc.add(1000000, 2000000))  # Deve imprimir 3000000
    print(calc.subtract(5000000, 2000000))  # Deve imprimir 3000000
    print(calc.call_counts)  # Deve imprimir {'add': 1, 'subtract': 1}

elif test_case == "negative_values":
    calc = Calculator()
    
    print(calc.add(-5, -3))  # Deve imprimir -8
    print(calc.subtract(-10, -4))  # Deve imprimir -6
    print(calc.call_counts)  # Deve imprimir {'add': 1, 'subtract': 1}

elif test_case == "float_values":
    calc = Calculator()
    
    print(calc.add(5.5, 3.2))  # Deve imprimir 8.7
    print(calc.subtract(10.5, 4.2))  # Deve imprimir 6.3
    print(calc.call_counts)  # Deve imprimir {'add': 1, 'subtract': 1}

elif test_case == "zero_values":
    calc = Calculator()
    
    print(calc.add(0, 0))  # Deve imprimir 0
    print(calc.subtract(0, 0))  # Deve imprimir 0
    print(calc.call_counts)  # Deve imprimir {'add': 1, 'subtract': 1}

elif test_case == "counter_reset":
    calc = Calculator()
    
    print(calc.add(5, 3))  # Deve imprimir 8
    print(calc.call_counts)  # Deve imprimir {'add': 1, 'subtract': 0}
    
    # Redefinir o contador
    calc.call_counts = {"add": 0, "subtract": 0}
    
    print(calc.add(2, 7))  # Deve imprimir 9
    print(calc.call_counts)  # Deve imprimir {'add': 0, 'subtract': 0}

elif test_case == "counter_manipulation":
    calc = Calculator()
    
    print(calc.add(5, 3))  # Deve imprimir 8
    print(calc.call_counts)  # Deve imprimir {'add': 1, 'subtract': 0}
    
    # Manipular o contador diretamente
    calc.call_counts["add"] = 100
    
    print(calc.add(2, 7))  # Deve imprimir 9
    print(calc.call_counts)  # Deve imprimir {'add': 101, 'subtract': 0}

elif test_case == "performance_test":
    calc = Calculator()
    
    # Realizar 1000 operações de adição
    for i in range(1000):
        calc.add(i, i+1)
    
    # Realizar 500 operações de subtração
    for i in range(500):
        calc.subtract(i+10, i)
    
    print(calc.call_counts)  # Deve imprimir {'add': 1000, 'subtract': 500}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Object Oriented Programming

Pratique por conta própria: Compilador de Python online