Módulo 10 - Orientação a objetos avançada
Composição, herança e __slots__
10 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026
O que você vai aprender
- Distinguir composição de herança com a regra é um contra tem um.
- Reconhecer quando a herança modela mal um relacionamento.
- Usar __slots__ para economizar memória.
- Entender o que __slots__ cobra em troca da economia.
Ouvir o resumo desta aula
Um recap de cerca de 2 minutos na voz do Valim, para ouvir no trânsito ou na academia.
Ler a transcrição do resumo
Resumo da aula: Composição, herança e __slots__.
Os objetivos desta aula. Distinguir composição de herança com a regra é um contra tem um. Reconhecer quando a herança modela mal um relacionamento. Usar __slots__ para economizar memória. Entender o que __slots__ cobra em troca da economia.
Veja o essencial, parte por parte.
É um contra tem um. Herança modela é um: um Gato é um Animal.
Composição na prática. Você herda só para reaproveitar um ou dois métodos, sem que a filha seja de fato um tipo da mãe.
__slots__ e a economia de memória. O último recurso do módulo é uma otimização.
Esse foi o resumo do essencial. Para se aprofundar, leia a aula completa e responda os exercícios.
É um contra tem um
Herança é atraente porque parece resolver reúso de código de graça: herde e ganhe os métodos da base. Mas ela cria um laço forte entre a classe filha e a mãe, e usada onde não cabe gera hierarquias confusas. A pergunta que orienta a decisão é simples e velha conhecida: o novo tipo é um subtipo do outro, ou apenas contém o outro? Se um Gato é um Animal, a herança modela bem: o gato realmente é um caso particular de animal. Mas se um Carro tem um Motor, herdar Carro de Motor seria absurdo, porque o carro não é um tipo de motor; ele tem um. Aí a relação certa é composição.
Herança (é um)
- Gato é um Animal
- Retângulo é uma Forma
- A filha é um caso particular da mãe
- Reúsa por especialização
Composição (tem um)
- Carro tem um Motor
- Pedido tem uma lista de Itens
- A classe contém outra como componente
- Reúsa por delegação
Composição na prática
Na composição, em vez de herdar, a classe guarda um objeto de outra como atributo e delega a ele o que for da sua alçada. O Carro tem um Motor guardado em self.motor e, quando precisa ligar, chama self.motor.ligar(). A vantagem é o baixo acoplamento: o carro depende apenas da interface do motor, não da sua estrutura interna, e você pode trocar o tipo de motor sem reescrever o carro. É mais flexível que a herança, que amarra a filha à implementação da mãe. Por isso um conselho clássico de projeto é preferir composição a herança quando ambas parecem possíveis; a herança fica reservada aos casos claros de é um.
class Motor:
def __init__(self, potencia):
self.potencia = potencia
def ligar(self):
return f"motor de {self.potencia}cv ligado"
class Carro:
def __init__(self, modelo, potencia):
self.modelo = modelo
self.motor = Motor(potencia) # Carro TEM UM Motor (composicao)
def ligar(self):
return f"{self.modelo}: {self.motor.ligar()}"
c = Carro("Sedan", 120)
print(c.ligar()) # Sedan: motor de 120cv ligadoCarro compõe um Motor e delega a ele: baixo acoplamento, fácil de trocar o motor.
__slots__ e a economia de memória
O último recurso do módulo é uma otimização. Por padrão, cada instância no Python guarda seus atributos em um dicionário interno, o __dict__, o que dá muita flexibilidade: você pode acrescentar um atributo novo a um objeto a qualquer momento. Essa flexibilidade tem um custo de memória por instância, irrelevante para poucos objetos, mas pesado quando você cria milhões. O __slots__ é a resposta. Ao declarar __slots__ com a lista dos atributos permitidos, você diz ao Python para reservar espaço fixo para eles e dispensar o dicionário interno. O ganho de memória é substancial e, muitas vezes, o acesso aos atributos fica até um pouco mais rápido.
class PontoLivre:
def __init__(self, x, y):
self.x = x
self.y = y
class PontoSlots:
__slots__ = ("x", "y") # so estes atributos, sem __dict__
def __init__(self, x, y):
self.x = x
self.y = y
p = PontoSlots(1, 2)
print(p.x, p.y) # 1 2 (funciona normalmente)
# p.z = 3 # AttributeError: nao esta em __slots____slots__ fixa os atributos e remove o __dict__ por instância, economizando memória.
A economia vem com um preço, e é justo conhecê-lo antes de sair usando __slots__ em tudo. Como não há mais o dicionário interno, você não pode acrescentar atributos que não estejam na lista: tentar fazê-lo levanta um erro. Isso é ótimo quando você quer justamente travar a estrutura, mas atrapalha se o seu design conta com atributos dinâmicos. Há também sutilezas com herança e com alguns recursos que esperam o __dict__. A regra prática é: use __slots__ quando tiver muitas instâncias de uma classe simples e de estrutura fixa, e o consumo de memória for medido como um problema real. Fora desse cenário, a flexibilidade padrão costuma valer mais que a economia.
Teste rápido
O que o __slots__ oferece e o que ele cobra em troca?
Perguntas frequentes
- Qual é a regra rápida para escolher entre herança e composição?
- Use a frase de teste. Se é um descreve a relação, como um Gato é um Animal, a herança cabe. Se tem um descreve melhor, como um Carro tem um Motor, use composição. Na dúvida, prefira composição: ela acopla menos e deixa o código mais flexível para mudar depois.
- Por que preferir composição a herança quando ambas parecem servir?
- Porque a herança amarra a filha à implementação da mãe, e mudanças na base podem quebrar as filhas. A composição depende só da interface do componente, então você pode trocar ou ajustar o componente sem reescrever quem o usa. Esse acoplamento menor torna o sistema mais fácil de evoluir.
- Quando o __slots__ realmente vale a pena?
- Quando você cria muitas instâncias, na casa dos milhares ou milhões, de uma classe simples e de estrutura fixa, e mediu que o consumo de memória é um problema. Nesse cenário, a economia é expressiva. Para poucos objetos ou classes que precisam de atributos dinâmicos, o __slots__ atrapalha mais do que ajuda.
- Com __slots__ ainda posso usar property e métodos?
- Pode. O __slots__ restringe apenas os atributos de dados por instância, não os métodos nem as properties, que pertencem à classe. Um detalhe: se você quiser uma property com o mesmo nome de um slot, há conflito; a saída comum é guardar o dado em um slot com nome interno e expor a property sobre ele.
- Dataclasses combinam com __slots__?
- Sim. Versões modernas do Python permitem gerar dataclasses com slots, unindo a conveniência da dataclass com a economia de memória. Como as dataclasses são o tema do próximo bloco do curso, vale ter em mente essa combinação para quando precisar de muitos objetos leves e bem estruturados ao mesmo tempo.
Fontes
Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.