Mixins
Parte da seção Object Oriented Programming do Journey de Python da Coddy — lição 38 de 64.
Mixins são um tipo especial de herança múltipla usado para "misturar" funcionalidades adicionais às classes. Eles fornecem métodos específicos sem serem classes completas por si mesmos.
Aqui está um exemplo de um mixin simples:
class JSONSerializableMixin:
def to_json(self):
import json
return json.dumps(self.__dict__)Vamos detalhar o que este mixin faz:
self.__dict__- Este atributo especial contém um dicionário de todos os atributos do objeto e seus valoresjson.dumps()- Esta função converte o dicionário Python em uma string formatada em JSON- O mixin "mistura" (mixes in) esta funcionalidade de serialização JSON em qualquer classe que herde dele
Agora vamos vê-lo em ação:
class User(JSONSerializableMixin):
def __init__(self, name, email):
self.name = name
self.email = emailO mixin adiciona funcionalidade JSON a qualquer classe que herde dele:
user = User("Alice", "alice@example.com")
print(user.to_json())Saída:
{"name": "Alice", "email": "alice@example.com"}A principal percepção: a classe User agora possui o método to_json() sem defini-lo diretamente. O mixin "incorporou" essa funcionalidade!
Crie múltiplos mixins para diferentes funcionalidades:
class PrintableMixin:
def pretty_print(self):
for key, value in self.__dict__.items():
print(f"{key}: {value}")
class ComparableMixin:
def __eq__(self, other):
return self.__dict__ == other.__dict__Cada mixin acessa self.__dict__ para trabalhar com os atributos do objeto, independentemente de qual classe use o mixin. Este é o poder dos mixins - eles fornecem funcionalidade reutilizável que funciona com os atributos de qualquer classe.
Combine múltiplos mixins em uma única classe:
class Product(JSONSerializableMixin, PrintableMixin, ComparableMixin):
def __init__(self, name, price):
self.name = name
self.price = price
product1 = Product("Laptop", 999)
product2 = Product("Laptop", 999)Use todas as funcionalidades do mixin:
print(product1.to_json()) # De JSONSerializableMixin
product1.pretty_print() # De PrintableMixin
print(product1 == product2) # De ComparableMixinSaída:
{"name": "Laptop", "price": 999}
name: Laptop
price: 999
TrueCaracterísticas principais de mixins:
- Não se destinam a ser instanciados por conta própria
- Fornecem funcionalidade específica e reutilizável
- Geralmente não possuem métodos
__init__ - Os nomes geralmente terminam com "Mixin" ou "able"
- Podem ser combinados com herança múltipla
- Trabalham com
self.__dict__ou outros recursos comuns de objetos para serem flexíveis
Ponto Chave: Mixins fornecem uma maneira de compartilhar funcionalidade entre diferentes hierarquias de classes sem criar árvores de herança complexas. Eles permitem que você "misture" capacidades específicas como serialização, comparação ou impressão em qualquer classe que precise delas. Isso promove a reutilização de código e mantém as classes focadas em suas responsabilidades primárias.
Desafio
MédioNeste desafio, você implementará um sistema simples de e-commerce usando mixins e herança.
Implemente as classes necessárias nestes arquivos (siga os comentários TODO em cada arquivo):
printablemixin.py- Crie o PrintableMixin com funcionalidade de saída formatadadiscountmixin.py- Implemente o DiscountMixin para cálculos de redução de preçoshippablemixin.py- Construa o ShippableMixin para recursos de peso e custo de envioproduct.py- Desenvolva a classe base Product com a herança de mixin apropriadaphysicalproduct.py- Crie a classe PhysicalProduct estendendo Productdigitalproduct.py- Implemente a classe DigitalProduct com comportamento de desconto especial
Folha de consulta
Mixins são um tipo especial de herança múltipla usado para "misturar" (mix in) funcionalidades adicionais às classes sem que elas sejam classes completas por si mesmas.
Exemplo básico de mixin:
class JSONSerializableMixin:
def to_json(self):
import json
return json.dumps(self.__dict__)
class User(JSONSerializableMixin):
def __init__(self, name, email):
self.name = name
self.email = emailMúltiplos mixins podem ser combinados:
class PrintableMixin:
def pretty_print(self):
for key, value in self.__dict__.items():
print(f"{key}: {value}")
class ComparableMixin:
def __eq__(self, other):
return self.__dict__ == other.__dict__
class Product(JSONSerializableMixin, PrintableMixin, ComparableMixin):
def __init__(self, name, price):
self.name = name
self.price = pricePrincipais características dos mixins:
- Não foram feitos para serem instanciados por conta própria
- Fornecem funcionalidades específicas e reutilizáveis
- Geralmente não possuem métodos
__init__ - Os nomes geralmente terminam com "Mixin" ou "able"
- Podem ser combinados com herança múltipla
Experimente você mesmo
from product import Product
from physicalproduct import PhysicalProduct
from digitalproduct import DigitalProduct
from printablemixin import PrintableMixin
from discountmixin import DiscountMixin
from shippablemixin import ShippableMixin
def test_basic_functionality():
# Testa a funcionalidade básica de todas as classes
p = Product("Laptop", 1000)
assert p.print_details() == "Product: Laptop, Price: $1000", f"Print details failed: {p.print_details()}"
assert p.apply_discount(10) == 900, f"Discount calculation failed: {p.apply_discount(10)}"
physical = PhysicalProduct("Desk", 500)
physical.set_weight(30)
assert physical.calculate_shipping() == 15, f"Shipping calculation failed: {physical.calculate_shipping()}"
digital = DigitalProduct("Software", 200)
assert digital.apply_discount(10) == 180, f"Digital discount failed: {digital.apply_discount(10)}"
print("Basic functionality test passed!")
def test_edge_cases():
# Testa casos extremos como valores zero e negativos
p = Product("Free Item", 0)
assert p.apply_discount(10) == 0, f"Zero price discount failed: {p.apply_discount(10)}"
p_neg = Product("Negative Item", -100)
assert p_neg.apply_discount(10) == -90, f"Negative price discount failed: {p_neg.apply_discount(10)}"
physical = PhysicalProduct("Empty Box", 10)
physical.set_weight(0)
assert physical.calculate_shipping() == 0, f"Zero weight shipping failed: {physical.calculate_shipping()}"
physical_neg = PhysicalProduct("Anti-Gravity Item", 10)
physical_neg.set_weight(-5)
assert physical_neg.calculate_shipping() == -2.5, f"Negative weight shipping failed: {physical_neg.calculate_shipping()}"
print("Edge cases test passed!")
def test_large_values():
# Testa com valores muito grandes
p = Product("Expensive Item", 1000000)
assert p.apply_discount(10) == 900000, f"Large value discount failed: {p.apply_discount(10)}"
physical = PhysicalProduct("Heavy Item", 500)
physical.set_weight(1000)
assert physical.calculate_shipping() == 500, f"Large weight shipping failed: {physical.calculate_shipping()}"
print("Large values test passed!")
def test_inheritance():
# Testa relações de herança
p = Product("Test", 100)
physical = PhysicalProduct("Test", 100)
digital = DigitalProduct("Test", 100)
assert isinstance(p, PrintableMixin), "Product should inherit from PrintableMixin"
assert isinstance(p, DiscountMixin), "Product should inherit from DiscountMixin"
assert isinstance(physical, Product), "PhysicalProduct should inherit from Product"
assert isinstance(physical, ShippableMixin), "PhysicalProduct should inherit from ShippableMixin"
assert isinstance(physical, PrintableMixin), "PhysicalProduct should inherit from PrintableMixin through Product"
assert isinstance(physical, DiscountMixin), "PhysicalProduct should inherit from DiscountMixin through Product"
assert isinstance(digital, Product), "DigitalProduct should inherit from Product"
assert isinstance(digital, PrintableMixin), "DigitalProduct should inherit from PrintableMixin through Product"
assert isinstance(digital, DiscountMixin), "DigitalProduct should inherit from DiscountMixin through Product"
print("Inheritance test passed!")
def test_method_overriding():
# Testa o comportamento de sobrescrita de método
p = Product("Regular Product", 100)
digital = DigitalProduct("Digital Product", 100)
# Mesmo preço, mesma porcentagem de desconto, resultados diferentes
assert p.apply_discount(20) == 80, f"Regular discount calculation failed: {p.apply_discount(20)}"
assert digital.apply_discount(20) == 90, f"Digital fixed discount failed: {digital.apply_discount(20)}"
# Produto digital deve sempre aplicar 10% de desconto independentemente do parâmetro
assert digital.apply_discount(0) == 90, "Digital product should apply 10% discount even with 0%"
assert digital.apply_discount(50) == 90, "Digital product should apply 10% discount even with 50%"
print("Method overriding test passed!")
def test_polymorphism():
# Testa o comportamento polimórfico com uma lista de diferentes tipos de produtos
products = [
Product("Regular", 100),
PhysicalProduct("Physical", 100),
DigitalProduct("Digital", 100)
]
# Define o peso para o produto físico
products[1].set_weight(10)
# Resultados esperados para apply_discount(20)
expected_discounts = [80, 80, 90]
for i, product in enumerate(products):
# Todos devem ter o método print_details
assert "Product:" in product.print_details(), f"Polymorphic print_details failed for {type(product)}"
# Todos devem ter o método apply_discount, mas com implementações diferentes
assert product.apply_discount(20) == expected_discounts[i], f"Polymorphic apply_discount failed for {type(product)}"
print("Polymorphism test passed!")
def test_attribute_access():
# Testa padrões de acesso a atributos
p = Product("Test Product", 100)
assert p.name == "Test Product", "Name attribute not properly set in Product"
assert p.price == 100, "Price attribute not properly set in Product"
physical = PhysicalProduct("Physical Product", 200)
assert physical.name == "Physical Product", "Name attribute not properly set in PhysicalProduct"
assert physical.price == 200, "Price attribute not properly set in PhysicalProduct"
# O atributo weight não deve existir antes de set_weight ser chamado
try:
weight = physical.weight
assert False, "Weight attribute should not exist before set_weight is called"
except AttributeError:
pass
physical.set_weight(15)
assert physical.weight == 15, "Weight attribute not properly set in PhysicalProduct"
digital = DigitalProduct("Digital Product", 300)
assert digital.name == "Digital Product", "Name attribute not properly set in DigitalProduct"
assert digital.price == 300, "Price attribute not properly set in DigitalProduct"
print("Attribute access test passed!")
# Executor de testes principal
test_case = input()
if test_case == "basic_test":
test_basic_functionality()
elif test_case == "edge_cases":
test_edge_cases()
elif test_case == "large_values":
test_large_values()
elif test_case == "inheritance":
test_inheritance()
elif test_case == "method_overriding":
test_method_overriding()
elif test_case == "polymorphism":
test_polymorphism()
elif test_case == "attribute_access":
test_attribute_access()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