W3docs

Herança em Python

Aprenda herança em Python: subclasses, substituição de métodos, super(), herança múltipla, MRO e verificações com isinstance — com exemplos executáveis.

Compreendendo a Herança em Python

Herança permite construir uma nova classe a partir de uma já existente. A nova classe (a subclasse ou classe filha) recebe automaticamente todos os atributos e métodos da classe existente (a superclasse, classe base ou classe pai). Em seguida, você pode adicionar comportamentos extras ou substituir seletivamente o que foi herdado.

Este capítulo aborda:

  • A sintaxe para criar subclasses
  • Como super() funciona e por que você deve usá-lo
  • Substituição de métodos e chamada da versão do pai
  • Herança simples, multinível e múltipla
  • A Ordem de Resolução de Métodos (MRO) do Python
  • isinstance() e issubclass() para verificações de tipo em tempo de execução
  • Erros comuns

Antes de ler este capítulo, certifique-se de que está familiarizado com classes e objetos em Python. Para outros pilares de POO relacionados, consulte Encapsulamento em Python e Classes Abstratas em Python.

A Sintaxe da Herança

Para criar uma subclasse, escreva o nome da classe pai dentro de parênteses após o nome da nova classe:

class ParentClass:
    pass

class ChildClass(ParentClass):
    pass

Um exemplo mínimo, mas concreto, com a classe base Vehicle e a subclasse Car:

class Vehicle:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year

    def start(self):
        print(f"{self.make} {self.model} started.")

    def stop(self):
        print(f"{self.make} {self.model} stopped.")


class Car(Vehicle):
    def __init__(self, make, model, year, num_doors=4):
        super().__init__(make, model, year)   # delegate to Vehicle.__init__
        self.num_doors = num_doors


camry = Car("Toyota", "Camry", 2023)
camry.start()   # Toyota Camry started.
camry.stop()    # Toyota Camry stopped.
print(camry.num_doors)  # 4

Car herda start() e stop() de Vehicle sem duplicá-los. Adicionar num_doors é a única nova responsabilidade que Car precisa tratar.

Usando super()

super() retorna um objeto proxy que delega chamadas de métodos para a classe pai. O uso mais comum é dentro de __init__ para inicializar os atributos do pai antes de adicionar os do filho:

class Car(Vehicle):
    def __init__(self, make, model, year, num_doors=4):
        super().__init__(make, model, year)  # runs Vehicle.__init__
        self.num_doors = num_doors

Por que preferir super() em vez de escrever Vehicle.__init__(self, ...) diretamente?

  • Manutenção: se você renomear ou substituir a classe pai, só precisa alterar uma linha.
  • Herança múltipla: super() segue a Ordem de Resolução de Métodos (MRO) do Python, garantindo que cada classe na cadeia seja chamada corretamente. Codificar o nome do pai diretamente ignora essa lógica.

Você pode chamar super() para qualquer método, não apenas __init__:

class Car(Vehicle):
    def start(self):
        super().start()                               # call Vehicle.start first
        print(f"({self.num_doors}-door model ready)")

Substituição de Métodos

Uma subclasse substitui um método do pai definindo um método com o mesmo nome. Python sempre chama primeiro a versão mais derivada:

class Vehicle:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year

    def start(self):
        print(f"{self.make} {self.model} started.")

    def stop(self):
        print(f"{self.make} {self.model} stopped.")


class Car(Vehicle):
    def start(self):
        print(f"{self.make} {self.model} revved the engine and started.")


camry = Car("Toyota", "Camry", 2023)
camry.start()  # Toyota Camry revved the engine and started.
camry.stop()   # Toyota Camry stopped.  (inherited, not overridden)

Quando você deseja estender em vez de substituir o comportamento do pai, chame super() dentro da substituição:

class Car(Vehicle):
    def start(self):
        super().start()                         # Vehicle.start runs first
        print("Seatbelt reminder: buckle up!")  # then add the extra step

Herdando Atributos de Classe

A herança também se aplica a atributos de classe (compartilhados entre instâncias). Uma subclasse pode substituí-los da mesma forma que substitui métodos:

class Vehicle:
    wheels = 4

class Motorcycle(Vehicle):
    wheels = 2   # override the class attribute

class Car(Vehicle):
    pass         # inherits wheels = 4


print(Motorcycle.wheels)  # 2
print(Car.wheels)         # 4
print(Vehicle.wheels)     # 4

Tipos de Herança

Herança Simples

Um filho, um pai — o padrão mais comum.

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

    def speak(self):
        return f"{self.name} makes a sound."


class Dog(Animal):
    def speak(self):
        return f"{self.name} says woof!"


dog = Dog("Rex")
print(dog.speak())  # Rex says woof!

Herança Multinível

Uma subclasse pode, por sua vez, ser subclassificada, criando uma cadeia:

class Vehicle:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year

    def stop(self):
        print(f"{self.make} {self.model} stopped.")


class Car(Vehicle):
    def __init__(self, make, model, year, num_doors=4):
        super().__init__(make, model, year)
        self.num_doors = num_doors

    def start(self):
        print(f"{self.make} {self.model} started.")


class ElectricCar(Car):
    def __init__(self, make, model, year, range_km):
        super().__init__(make, model, year)   # calls Car.__init__
        self.range_km = range_km

    def start(self):
        print(f"{self.make} {self.model} powered up silently.")

    def charge(self):
        print(f"Charging... range is {self.range_km} km.")


tesla = ElectricCar("Tesla", "Model 3", 2024, 580)
tesla.start()   # Tesla Model 3 powered up silently.
tesla.charge()  # Charging... range is 580 km.
tesla.stop()    # Tesla Model 3 stopped.  (inherited from Vehicle)

ElectricCar está dois níveis abaixo de Vehicle. Cada chamada a super().__init__ sobe um nível na cadeia, fazendo com que todos os três métodos __init__ sejam executados.

Herança Múltipla

Python permite que uma classe herde de mais de um pai. Liste-os entre parênteses, separados por vírgulas:

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

    def speak(self):
        return f"{self.name} makes a sound."


class Swimmer:
    def swim(self):
        return f"{self.name} swims."


class Flyer:
    def fly(self):
        return f"{self.name} flies."


class Duck(Animal, Swimmer, Flyer):
    def speak(self):
        return f"{self.name} says quack!"


donald = Duck("Donald")
print(donald.speak())  # Donald says quack!
print(donald.swim())   # Donald swims.
print(donald.fly())    # Donald flies.

A herança múltipla é poderosa, mas introduz complexidade. Use mixins — classes pequenas com propósito único que adicionam uma única capacidade — para mantê-la gerenciável. Swimmer e Flyer acima são exatamente esse padrão.

A Ordem de Resolução de Métodos (MRO)

Quando Python procura um método ou atributo, segue uma ordem de busca determinística chamada Ordem de Resolução de Métodos (MRO). Para herança simples, a ordem é óbvia (filho → pai → avô → object). Para herança múltipla, Python usa o algoritmo de linearização C3 para produzir uma ordem inequívoca.

Inspecione o MRO de qualquer classe com .__mro__ ou help():

print(Duck.__mro__)
# (<class 'Duck'>, <class 'Animal'>, <class 'Swimmer'>, <class 'Flyer'>, <class 'object'>)

O MRO importa mais quando vários pais definem o mesmo método. Python escolhe a primeira classe no MRO que o define. É por isso que super() é importante em hierarquias de herança múltipla: cada classe no MRO chama super(), dando a todas as classes na cadeia a oportunidade de executar.

class Base:
    def greet(self):
        print("Hello from Base")


class Left(Base):
    def greet(self):
        print("Hello from Left")
        super().greet()


class Right(Base):
    def greet(self):
        print("Hello from Right")
        super().greet()


class Child(Left, Right):
    def greet(self):
        print("Hello from Child")
        super().greet()


Child().greet()
# Hello from Child
# Hello from Left
# Hello from Right
# Hello from Base

Cada chamada a super() segue o MRO (Child → Left → Right → Base), portanto cada greet() é executado exatamente uma vez, mesmo que Base apareça como pai tanto de Left quanto de Right. Isso é o problema do diamante — o MRO do Python o resolve de forma elegante.

Verificando a Herança em Tempo de Execução

isinstance(obj, cls)

Retorna True se obj for uma instância de cls ou de qualquer uma de suas subclasses:

tesla = ElectricCar("Tesla", "Model 3", 2024, 580)

print(isinstance(tesla, ElectricCar))  # True
print(isinstance(tesla, Car))          # True  — Car is a parent
print(isinstance(tesla, Vehicle))      # True  — Vehicle is a grandparent
print(isinstance(tesla, str))          # False

Isso é mais confiável do que comparar type(obj) == Car, que retorna False para subclasses.

issubclass(sub, cls)

Retorna True se sub for uma subclasse de cls (inclusive ela mesma):

print(issubclass(ElectricCar, Vehicle))  # True
print(issubclass(Car, ElectricCar))      # False
print(issubclass(Car, Car))              # True  — a class is a subclass of itself

Erros Comuns

Esquecer de chamar super().__init__()

Se o __init__ do filho não chamar super().__init__(), os atributos do pai nunca serão definidos. Qualquer método que os espere lançará AttributeError:

class Car(Vehicle):
    def __init__(self, make, model, year, num_doors=4):
        # forgot super().__init__(...)
        self.num_doors = num_doors

c = Car("Toyota", "Camry", 2023)
c.start()  # AttributeError: 'Car' object has no attribute 'make'

Substituir sem chamar super() quando a intenção era estender

Se você substituir um método e esquecer super(), a versão do pai nunca será executada. Isso silenciosamente descarta comportamentos dos quais outros trechos de código podem depender.

Hierarquias profundas de herança múltipla

Mais de dois níveis de herança múltipla é muito difícil de raciocinar. Se você se encontrar escrevendo class Foo(A, B, C, D), considere usar composição — armazenando instâncias como atributos — em vez disso.

Benefícios da Herança

  1. Reutilização de código — a lógica compartilhada vive em um único lugar; as subclasses a herdam gratuitamente.
  2. Extensibilidade — você pode adicionar ou alterar comportamentos em uma subclasse sem modificar o pai, mantendo os chamadores existentes inalterados.
  3. Polimorfismo — funções que aceitam um Vehicle funcionam igualmente bem com qualquer Car, Motorcycle ou ElectricCar. Consulte Polimorfismo em Python para o quadro completo.

Prática

Prática
Which of the following statements about Python inheritance are correct according to the information provided on the specified URL?
Which of the following statements about Python inheritance are correct according to the information provided on the specified URL?
Was this page helpful?