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.
Folha de consulta
Decoradores de classe modificam ou aprimoram classes ao envolvê-las com outra função. Eles recebem uma classe como entrada, a modificam e retornam a classe modificada.
Sintaxe básica de um decorador de classe:
def decorator_name(cls):
# Modify the class
return cls
@decorator_name
class MyClass:
passExemplo - adicionando um método a uma classe:
def add_farewell(cls):
def farewell(self):
return f"Goodbye from {self.name}"
cls.farewell = farewell # Add the method to the class
return cls
@add_farewell
class EnhancedPerson:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"
person = EnhancedPerson("Alice")
print(person.greet()) # Hello, my name is Alice
print(person.farewell()) # Goodbye from AliceExemplo - envolvendo um método existente (armazenar o original e depois substituir):
def loud_greet(cls):
original_greet = cls.greet # Store the original method
def new_greet(self):
result = original_greet(self) # Call the original
return result.upper() # Enhance the result
cls.greet = new_greet # Replace with the wrapped version
return cls
@loud_greet
class EnhancedPerson:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"
person = EnhancedPerson("Alice")
print(person.greet()) # HELLO, MY NAME IS ALICEPassos principais para envolver um método existente:
original = cls.method_name — salva o método originaldef new_method(self): original(self) — chama-o dentro do wrappercls.method_name = new_method — substitui o método na classeDecoradores de classe podem adicionar métodos, modificar atributos ou envolver funcionalidades existentes sem usar herança.
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ário