programandoai

Herança e dunders: reaproveitando e personalizando comportamento

Um Gerente é um tipo de Funcionário, com tudo que um Funcionário já tem, mais um pouco. Reescrever essa parte comum do zero seria repetir código à toa.

Estudar esta lição interativa →

Numa folha de pagamento, um Gerente é, no fundo, um tipo específico de Funcionario: tem nome e salário como qualquer funcionário, mas também recebe um bônus que um funcionário comum não recebe. Reescrever nome e salário do zero dentro de Gerente duplicaria código que já existe em Funcionario. A herança evita isso: uma classe pode herdar tudo de outra, e só adicionar (ou personalizar) o que for realmente diferente.

class Funcionario:
    def __init__(self, nome, salario):
        self.nome = nome
        self.salario = salario

class Gerente(Funcionario):
    def __init__(self, nome, salario, bonus):
        super().__init__(nome, salario)
        self.bonus = bonus

    def salario_total(self):
        return self.salario + self.bonus

joao = Gerente("João", 5000, 1200)
print(joao.nome)
print(joao.salario_total())

super().__init__() reaproveita a inicialização de Funcionario dentro de Gerente.

class Gerente(Funcionario): declara que Gerente herda de Funcionario: todo método de Funcionario já está disponível em qualquer objeto Gerente, automaticamente. Dentro do __init__ de Gerente, super().__init__(nome, salario) chama o __init__ da classe-mãe, reaproveitando aquela lógica em vez de copiá-la.

Pense em herança como um filho herdando características do pai: o filho já nasce com boa parte do que o pai tem (algumas feições, alguns traços), mas também pode ter características próprias, adicionais, que o pai não tinha. Gerente "nasce" com tudo que Funcionario já oferece, e acrescenta o bônus por cima.

Um último recurso útil: por padrão, imprimir um objeto (print(funcionario)) mostra algo pouco legível, tipo <__main__.Funcionario object at 0x...>. O método especial (chamado de dunder, de "double underscore") __str__ permite personalizar exatamente o que aparece quando o objeto é convertido para texto ou impresso:

class Funcionario:
    def __init__(self, nome, salario):
        self.nome = nome
        self.salario = salario

    def __str__(self):
        return f"Funcionario({self.nome}, R$ {self.salario:.2f})"

joao = Funcionario("João", 5000)
print(joao)

__str__ é chamado automaticamente por print() e por str(objeto).

__str__ é só um entre vários dunders

Existem outros métodos especiais parecidos (__eq__ para comparar dois objetos com ==, __len__ para o objeto responder a len(objeto), entre outros), todos seguindo o mesmo padrão de duplo underscore no nome. __str__ é o mais comum de encontrar no dia a dia, por deixar prints de depuração muito mais legíveis.

Esquecer super().__init__() deixa atributos da classe-mãe faltando

Se Gerente.__init__ não chamar super().__init__(nome, salario), o objeto Gerente nunca ganha os atributos self.nome e self.salario que Funcionario configuraria. Tentar usar gerente.nome depois disso gera AttributeError: 'Gerente' object has no attribute 'nome'. O sintoma costuma aparecer bem longe da causa (na hora de usar o atributo, não na hora de criar o objeto), o que torna esse um erro chato de rastrear se você não souber onde procurar primeiro.

class Funcionario:
    def __init__(self, nome, salario):
        self.nome = nome
        self.salario = salario

    def __str__(self):
        return f"Funcionario({self.nome}, R$ {self.salario:.2f})"

class Gerente(Funcionario):
    def __init__(self, nome, salario, bonus):
        super().__init__(nome, salario)
        self.bonus = bonus

ana = Gerente("Ana", 6000, 1500)
print(ana)

Crie o objeto ana e imprima-o diretamente.

Complete a classe ContaBancaria: __init__(self, saldo_inicial) guarda o saldo em self.saldo; depositar(self, valor) soma ao saldo; sacar(self, valor) subtrai do saldo, mas só se houver saldo suficiente (senão, não muda nada). O código já cria conta = ContaBancaria(100) ao final.
class ContaBancaria:
    def __init__(self, saldo_inicial):
        self.saldo = saldo_inicial

    def depositar(self, valor):
        pass

    def sacar(self, valor):
        pass

conta = ContaBancaria(100)

🧩 Este é um exercício com correção automática — abra no app para completá-lo e verificar.

← Voltar ao índice do curso