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 clsAplique 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 AliceVocê 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 é AliceOs 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.
Desafio
MédioNeste desafio, você implementará um decorador de classe que adiciona a funcionalidade de rastreamento de chamadas de métodos.
decorator.py- Implemente o decorador de classeadd_countercalculator.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}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
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosO Parâmetro selfMétodosAtributosMétodo Construtor (__init__)Recapitulação - Calculadora Simples4Herança
Herança BásicaA Função super()Sobrescrita de MétodoHerança MúltiplaOrdem de Resolução de MétodosRecapitulação - Hierarquia de Funcionários7Métodos Especiais
Introdução aos Métodos MágicosSobrecarga de OperadoresMétodos Mágicos de ContêinerRecapitulação - Lista Personalizada10Padrões de Projeto Parte 1
Introdução a Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão ObserverPadrão Strategy2Decoradores
Introdução aos DecoradoresDecorador de PropriedadeDecorador de Método EstáticoDecorador de Método de Classe5Polimorfismo
Sobrescrita de Método RevisitadaDuck TypingClasses e Métodos AbstratosDesign de InterfacesRecapitulação - Calculadora de Formas8Conceitos Avançados de POO
Composição vs HerançaMixinsMétodos Estáticos e de ClasseDecoradores de ClasseGerenciadores de Contexto11Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão Composite3Propriedades de Classe
Variáveis de Instância vs de ClasseDecoradores de PropriedadeAtributos PrivadosRecapitulação - Gerenciador de Conta Bancária6Encapsulamento
Membros Públicos, Protegidos e PrivadosModificadores de AcessoOcultação de InformaçãoProperty Decorators AvançadosRecapitulação - Sistema de Registro de Alunos12Projeto: Gerenciamento de Biblioteca
Visão Geral do ProjetoClasses Livro e UsuárioSistema de EmpréstimosFuncionalidade de BuscaInterface AdministrativaTestes e IntegraçãoPratique por conta própria: Compilador de Python online