Herança

A herança é um princípio da Programação Orientada a Objetos (POO) que permite que uma classe (subclasse) herde atributos e métodos de outra classe (superclasse), promovendo reutilização de código e organização hierárquica.

Tipos de Herança

  1. Simples: Uma subclasse herda de uma única superclasse.
  2. Múltipla: Uma subclasse herda de várias superclasses.
  3. Multinível: Herança em cadeia (ex.: ClasseC → ClasseB → ClasseA).

Herança simples

class Animal:
    def __init__(self, nome):
        self.nome = nome

    def emitir_som(self):
        print("Som genérico")

class Cachorro(Animal):  # Herda de Animal
    def emitir_som(self):  # Sobrescreve o método
        print("Au au!")

# Uso
rex = Cachorro("Rex")
rex.emitir_som()  # Saída: "Au au!"
  • Cachorro herda o atributo nome e o método emitir_som de Animal.
  • O método emitir_som é sobrescrito para personalizar o comportamento.

Herança multipla

class Voar:
    def voar(self):
        print("Voando...")

class Nadar:
    def nadar(self):
        print("Nadando...")

class Pato(Voar, Nadar):  # Herda de Voar e Nadar
    pass

# Uso
pato = Pato()
pato.voar()  # Saída: "Voando..."
pato.nadar()  # Saída: "Nadando..."
  • Pato herda métodos de ambas as classes (Voar e Nadar).
  • Útil para combinar funcionalidades de classes distintas.

Herança multinivel

class Veiculo:
    def __init__(self, marca):
        self.marca = marca

class Carro(Veiculo):
    def __init__(self, marca, modelo):
        super().__init__(marca)
        self.modelo = modelo

class CarroEletrico(Carro):  # Herda de Carro (que herda de Veiculo)
    def __init__(self, marca, modelo, bateria):
        super().__init__(marca, modelo)
        self.bateria = bateria

# Uso
tesla = CarroEletrico("Tesla", "Model S", "100kWh")
print(tesla.bateria)  # Saída: "100kWh"
  • CarroEletrico herda de Carro, que por sua vez herda de Veiculo.
  • Cada nível adiciona novos atributos (modelobateria).

super()

A função super() em Python é usada para aceder a métodos e atributos de uma superclasse (classe pai) em uma relação de herança.

Exemplo 1:

class Animal:
    def __init__(self, nome):
        self.nome = nome
        print(f"Animal {self.nome} criado")

    def emitir_som(self):
        return "Som genérico"

class Cachorro(Animal):
    def __init__(self, nome, raca):
        super().__init__(nome)  # chama o construtor de Animal
        self.raca = raca
        print(f"Cachorro da raça {self.raca} criado")

    def emitir_som(self):
        return "Au au!"  # Sobrescreve o método da superclasse

rex = Cachorro("Rex", "Labrador")
print(rex.emitir_som())  # Saída: "Au au!"
  • super().__init__(nome) executa o construtor de Animal.
  • Cachorro adiciona o atributo raca e sobrescreve emitir_som().

Exemplo 2:

class Voar:
    def voar(self):
        print("Voando...")

class Nadar:
    def nadar(self):
        print("Nadando...")

class Pato(Voar, Nadar):
    def __init__(self):
        super().voar()  # Chama voar() de Voar
        super().nadar()  # Chama nadar() de Nadar

pato = Pato()  
# Output: 
# Voando...
# Nadando...

Exemplo 3:

class Pessoa:
    def __init__(self, nome):
        self.nome = nome

    def apresentar(self):
        return f"Olá, sou {self.nome}"

class Aluno(Pessoa):
    def apresentar(self):
        return super().apresentar() + " e sou aluno."

aluno = Aluno("Maria")
print(aluno.apresentar())  # Saída: "Olá, sou Maria e sou aluno."

super().apresentar() reutiliza o método da superclasse antes de adicionar novo conteúdo.