Pular para o conteúdo principal

Herança Python: Práticas recomendadas para código reutilizável

A herança Python permite que você crie novas classes reutilizando e ampliando a funcionalidade das classes existentes. Aprenda a projetar relacionamentos entre classes pai-filho, implementar padrões de herança e aplicar técnicas como a substituição de métodos.
Actualizado 12 de fev. de 2025  · 9 min de leitura

Imagine que você está criando um sistema de software com várias funções de usuário, como alunos, professores e administradores. Essas funções compartilham atributos comuns, como nome e ID, mas também exigem funcionalidades que são exclusivas para elas. Em vez de duplicar o código, a herança permite que você defina o comportamento compartilhado em uma classe principal e o estenda em classes secundárias especializadas.

Neste artigo, exploraremos a herança Python, abordando conceitos básicos e avançados, como a substituição de métodos e a função super(), que é uma função integrada que retorna um objeto temporário da superclasse, , para que você possa acessar seus métodos sem nomear explicitamente a classe pai. Não se preocupe se isso ainda não fizer sentido, pois aprenderemos tudo sobre isso a seguir.

Aprenda Python do zero

Domine o Python para a ciência de dados e adquira habilidades que estão em alta.
Comece a aprender de graça

Noções básicas de herança em Python

A herança é um dos pilares fundamentais da programação orientada a objetos (OOP) que permite que uma classe (chamada de classe filha) derive atributos e métodos de outra classe (chamada de classe pai). Esse recurso é essencial para areutilização do código e simplifica a manutenção, facilitando a criação de programas dimensionáveis e eficientes.

Definição de classes pai e filho

Antes de prosseguir, vamos explorar a relação entre as classes pai e filha.

Classe pai

Vamos começar com a classe principal. Uma classe pai é a classe base da qual derivam as classes filhas. Ele encapsula atributos e métodos compartilhados.

Usando o Python, veja como definimos uma classe principal:

class ParentClass:
    def __init__(self, attributes):
        # Initialize attributes
        pass

    def method(self):
        # Define behavior
        pass

Classe infantil 

Uma classe filha herda atributos e métodos da classe pai. Isso permite que você use a funcionalidade definida na classe principal. O código a seguir mostra como uma classe filha herda atributos e métodos de uma classe pai:

class ChildClass(ParentClass):
    def additional_method(self):
        # Define new behavior
        pass

Essa sintaxe simples permite que a classe filha utilize e estenda a funcionalidade definida na classe pai.

Criando uma classe pai e uma classe filha

Vamos criar um exemplo prático com uma classe Person como pai e uma classe Student como filho.

Criando a classe pai

A classe Person contém atributos compartilhados e um método para exibir informações:    

# Defining the Parent Class
class Person:
    def __init__(self, name, id):
        self.name = name
        self.id = id
    def display_info(self):
        return f"Name: {self.name}, ID: {self.id}"

Criando a classe filha

A classe Student herda de Person e adiciona um novo método study.

# Defining the Child Class
class Student(Person):
    def study(self):
        return f"{self.name} is studying."

Vamos testar as classes pai e filho.

# Creating and Testing Instances
student = Student("Samuel", 102)
print(student.display_info())  
print(student.study())      
Name: Samuel, ID: 102
Samuel is studying.

O que está acontecendo é o seguinte:

  1. A classe Student usa o método __init__ de Person para inicializar name e id.

  2. O método study é exclusivo da classe Student, ampliando sua funcionalidade.

  3. O método display_info é herdado diretamente de Person.

Tipos de herança em Python

A herança em Python permite que as classes herdem atributos e comportamentos de outras classes, promovendo a reutilização do código e o design limpo, conforme falamos anteriormente. Nesta seção, podemos falar sobre os diferentes tipos de herança Python, que incluem herança única, múltipla, hierárquica e híbrida como categorias separadas.

Herança única

A herança única ocorre quando uma classe filha herda de uma única classe pai, permitindo que ela estenda a funcionalidade da classe pai. Isso é útil quando um tipo de objeto compartilha propriedades comuns com uma categoria mais ampla, mas também requer atributos ou comportamentos adicionais.

O exemplo que comecei a analisar anteriormente era de herança única, mas agora vamos dar uma olhada um pouco mais de perto: Em um sistema de gestão escolar, todos os indivíduos, incluindo alunos, professores e funcionários, compartilham alguns detalhes comuns, como name e ID. No entanto, os alunos também têm registros acadêmicos, como notas e cursos matriculados. Usando a herança única, podemos criar uma classe Person para atributos compartilhados e estendê-la com uma classeStudent para detalhes acadêmicos.

herança única

Herança única. Imagem do autor

Aqui está um bom exemplo do cenário acima:

class Person:
    """Represents a general person with basic details."""
    def __init__(self, name, id):
        self.name = name
        self.id = id

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}"

class Student(Person):
    """Represents a student, extending the Person class to include academic details."""
    def __init__(self, name, id, grade, courses):
        super().__init__(name, id)
        self.grade = grade
        self.courses = courses

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Grade: {self.grade}, Courses: {', '.join(self.courses)}"

# Example usage in a school system
student = Student("Samuel", 5678, "B+", ["Math", "Physics", "Computer Science"])
print(student.get_details())
Name: Samuel, ID: 5678, Grade: B+, Courses: Math, Physics, Computer Science

A classe Student herda o método get_details() de Person, mas o estende para incluir grade e courses. Esse é um bom exemplo de como a herança única promove o que é conhecido como código modular.

Herança múltipla

De certa forma, a herança múltipla, como uma árvore genealógica, permite que uma classe filha herde de mais de uma classe pai, combinando atributos e comportamentos de cada uma delas. Isso pode levar a possíveis conflitos, que o Python resolve usando a ordem de resolução de método (MRO).

herança múltipla

Herança múltipla. Imagem do autor

Dê uma olhada:

class Person:
    def get_details(self):
        return "Details of a person."

class Athlete:
    def get_skill(self):
        return "Athletic skills."

class Student(Person, Athlete):
    pass

# Example usage
student = Student()
print(student.get_details())
print(student.get_skill())
Details of a person.
Athletic skills.

Vemos que a classe Student herdou atributos e métodos de Person e Athlete. Sem nenhum esforço adicional, a classe Student tem acesso ao método get_details() da classe pai Person e ao método get_skill() da classe pai Athlete. Estamos combinando efetivamente a funcionalidade de várias fontes.

Entretanto, a herança de várias classes pode gerar conflitos. E se ambas as classes pai definirem um método ou atributo com o mesmo nome? Mencionei algo sobre a ordem de resolução de métodos anteriormente, mas deixe-me falar um pouco mais sobre isso. Ordem de resolução de métodos determina a ordem em que as classes são pesquisadas em busca de métodos e atributos. O MRO segue uma abordagem de profundidade primeiro, da esquerda para a direita.

Você pode visualizar o MRO de uma classe usando o atributo __mro__ ou o método mro():

print(Student.__mro__)
(<class '__main__.Student'>, <class '__main__.Person'>, <class '__main__.Athlete'>, <class 'object'>)

Herança multinível, hierárquica e híbrida

O Python também oferece suporte a estruturas de herança mais complexas. Mostrarei essas ideias mais complexas usando o mesmo exemplo.

Herança em vários níveis

A herança multinível ocorre quando uma classe filha herda de outra classe filha, e essa classe filha herda de uma classe pai. Isso cria uma cadeia de herança.

herança em vários níveis

Herança em vários níveis. Imagem do autor

Aqui está um bom exemplo:

class Person:
    def __init__(self, name, id):
        self.name = name
        self.id = id

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}"

class Student(Person):
    def __init__(self, name, id, grade):
        super().__init__(name, id)
        self.grade = grade

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Grade: {self.grade}"

class GraduateStudent(Student):
    def __init__(self, name, id, grade, thesis_title):
        super().__init__(name, id, grade)
        self.thesis_title = thesis_title

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Grade: {self.grade}, Thesis: {self.thesis_title}"

# Example usage
grad_student = GraduateStudent("Charlie", 91011, "A", "AI in Healthcare")
print(grad_student.get_details())
Name: Charlie, ID: 91011, Grade: A, Thesis: AI in Healthcare

Aqui, cada classe da cadeia acrescenta algo novo: Person gerencia nomes e IDs, Student inclui notas e GraduateStudent apresenta uma tese. Graças ao super().__init__(), reutilizamos a lógica de inicialização sem duplicar o código. É eficiente, organizado e garante que todos os níveis da "escada de herança", como eu penso, funcionem.

Herança hierárquica

Na herança hierárquica, várias classes filhas herdam de uma única classe mãe, permitindo o comportamento compartilhado entre subclasses com atributos exclusivos.

herança hierárquica

Herança hierárquica. Imagem do autor

Vamos dar uma olhada em um bom exemplo juntos:

class Person:
    def __init__(self, name, id):
        self.name = name
        self.id = id

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}"

class Student(Person):
    def __init__(self, name, id, grade):
        super().__init__(name, id)
        self.grade = grade

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Grade: {self.grade}"

class Teacher(Person):
    def __init__(self, name, id, subject):
        super().__init__(name, id)
        self.subject = subject

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Subject: {self.subject}"

# Example usage
student = Student("Samuel", 5678, "B+")
teacher = Teacher("Dr. Smith", 1234, "Math")
print(student.get_details())  
print(teacher.get_details())
Name: Samuel, ID: 5678, Grade: B+
Dr. Smith, ID: 1234, Subject: Math

Aqui, a classe Person serve como base, oferecendo atributos e métodos comuns (name, id e get_details). As classes Student e Teacher estendem essa funcionalidade adicionando suas propriedades exclusivas (grade e subject) e personalizando o método get_details para refletir seus contextos específicos.

Com essa abordagem, a funcionalidade compartilhada permanece em um único lugar (a classe Person ), enquanto o comportamento especializado é perfeitamente encapsulado nas subclasses.

Herança híbrida

A herança híbrida combina vários tipos de herança, como a herança multinível ou múltipla, para modelar relacionamentos mais complexos.

herança híbrida

Herança híbrida. Imagem do autor

Vamos dar uma olhada em um exemplo que mostra a complexidade da herança híbrida.

# Base class
class Person:
    def __init__(self, name, id):
        self.name = name
        self.id = id

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}"

# Intermediate class inheriting from the base class
class Employee(Person):
    def __init__(self, name, id, position):
        super().__init__(name, id)
        self.position = position

    def get_position(self):
        return f"Position: {self.position}"

# Another independent base class
class Athlete:
    def __init__(self, sport):
        self.sport = sport

    def get_sport(self):
        return f"Sport: {self.sport}"

# Derived class combining Employee and Athlete
class Student(Employee, Athlete):
    def __init__(self, name, id, position, grade, sport):
        Employee.__init__(self, name, id, position)
        Athlete.__init__(self, sport)
        self.grade = grade

    def get_grade(self):
        return f"Grade: {self.grade}"

# Example usage
student = Student("Samuel", 1234, "Intern", "A", "Soccer")
print(student.get_details())  # From Person
print(student.get_position())  # From Employee
print(student.get_grade())  # From Student
print(student.get_sport())  # From Athlete
Name: Samuel, ID: 1234
Position: Intern
Grade: A
Sport: Soccer

Neste exemplo, a classe Student demonstra a herança híbrida ao herdar atributos e métodos de Employee (que por sua vez herda de Person) e Athlete. Isso combinaa herança hierárquica de (em que Employee herda de Person) e a herança múltipla (em que Student herda de Employee e Athlete ).

Benefícios da herança em Python

Agora, é hora de ver os pontos fortes e fracos: 

Benefícios da herança

  1. Reutilização: Com a herança, você pode escrever o código uma vez na classe principal e reutilizá-lo nas classes secundárias. Usando o exemplo, tanto FullTimeEmployee quanto Contractor podem herdar um método get_details() da classe pai Employee.

  2. Simplicidade: A herança modela os relacionamentos de forma clara. Um bom exemplo é a classe FullTimeEmployee que "é um" tipo da classe pai Employee.

  3. Escalabilidade: Ele também adiciona novos recursos ou classes secundárias sem afetar o código existente. Por exemplo, podemos adicionar facilmente uma nova classe Intern como uma classe filha.

Possíveis limitações da herança

  1. Complexidade: Isso não é surpreendente, mas muitos níveis de herança podem tornar o código difícil de acompanhar. Por exemplo, se um Employee tiver muitas classes filhas, como Manager, Engineer, Intern, etc., você poderá ficar confuso.

  2. Dependência: As alterações em uma classe principal podem afetar involuntariamente todas as subclasses. Se você modificar Employee, por exemplo, poderá quebrar FullTimeEmployee ou Contractor.

  3. Uso indevido: O uso da herança quando ela não é a melhor opção pode complicar os projetos. Você não gostaria de criar uma solução em que Car herdasse de Boat apenas para reutilizar move(). O relacionamento não faz sentido.

Técnicas avançadas de herança em Python

Agora que já exploramos os conceitos básicos de herança, vamos examinar algumas técnicas avançadas. Essas técnicas, como substituição de métodos, super(), classes de base abstratas e polimorfismo, aumentam a flexibilidade do código e permitem padrões de design mais sofisticados. 

Substituição de métodos em python

A substituição de método permite que uma classe secundária forneça uma implementação específica para um método já definido em sua classe principal. Isso é útil quando o comportamento herdado não atende totalmente aos requisitos da classe filha.

class Person:
    def __init__(self, name, id):
        self.name = name
        self.id = id
    
    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}"

class Student(Person):
    def __init__(self, name, id, grade):
        super().__init__(name, id)
        self.grade = grade
    
    # Overriding the get_details method
    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Grade: {self.grade}"

# Example usage
student = Student("Samuel", 1234, "A")
print(student.get_details())
Name: Samuel, ID: 1234, Grade: A

Aqui, a classe Student substitui o método get_details() da classe Person para fornecer suas próprias implementações específicas. Isso permite que a classe filha tenha seu próprio comportamento e, ao mesmo tempo, siga o mesmo nome de método.

Então, por que substituímos? Substituímos porque queremos personalizar o comportamento herdado e também porque queremos adaptar a funcionalidade de um método pai aos requisitos exclusivos de uma classe filha.

Usando super() para inicialização do pai

A função super() é usada para chamar métodos na classe pai a partir da classe filha. Isso é particularmente útil quando você deseja estender ou modificar a funcionalidade de um método da classe principal, como o método do construtor __Init__().

Então, por que usamos a função super()? Usamos a função super porque queremos chamar e inicializar o construtor da classe principal e também porque queremos evitar nomear explicitamente a classe principal. Isso é útil, especialmente em casos de herança múltipla.

class Person:
    def __init__(self, name, id):
        self.name = name
        self.id = id

class Student(Person):
    def __init__(self, name, id, grade):
        # Using super() to initialize the parent class
        super().__init__(name, id)
        self.grade = grade

# Example usage
student = Student("Samuel", 5678, "B+")
print(student.name)
print(student.id)
print(student.grade)
Samuel
5678
B+

Aqui, a classe Student usa super().__init__(name, id) para chamar o método __init__ da classe pai Person, de modo que não é necessário repetir o código para inicializar os atributos name e id. A classe filha introduz, então, um atributo grade, que é específico da classe Student.

Classes básicas abstratas (ABCs)

Uma classe base abstrata (ABC) é uma classe que não pode ser usada diretamente para criar objetos. Seu objetivo é definir um conjunto comum de métodos que outras classes devem implementar. Portanto, os ABCs são úteis quando você deseja garantir que determinados métodos estejam sempre presentes nas classes filhas.

from abc import ABC, abstractmethod

class Person(ABC):
    @abstractmethod
    def get_details(self):
        pass

class Student(Person):
    def __init__(self, name, id, grade):
        self.name = name
        self.id = id
        self.grade = grade
    
    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Grade: {self.grade}"

# Example usage
student = Student("Hamilton", 7890, "A-")
print(student.get_details())
Name: Hamilton, ID: 7890, Grade: A-

A classe Person aqui é uma classe abstrata que exige que qualquer classe filha implemente o método get_details(). Esse método está sendo implementado posteriormente pela classe filha, Student.

Polimorfismo

Polimorfismo significa muitas formas. Em Python, isso permite que classes diferentes usem o mesmo nome de método, mas cada uma pode implementar esse método de uma maneira diferente.

O polimorfismo nos ajuda a escrever códigos que podem funcionar com objetos de classes diferentes, mesmo que essas classes tenham comportamentos diferentes:

class Person:
    def get_details(self):
        return "Details of a person."

class Student(Person):
    def get_details(self):
        return "Details of a student."

class Teacher(Person):
    def get_details(self):
        return "Details of a teacher."

# Example usage
def print_details(person):
    print(person.get_details())

student = Student()
teacher = Teacher()

print_details(student) 
print_details(teacher) 
Details of a student.
Details of a teacher.

Neste exemplo, a função print_details() pode aceitar qualquer objeto do tipo Person, mas chamará o método get_details() apropriado com base no fato de o objeto ser um Student ou um Teacher.

Erros comuns e práticas recomendadas

Embora a herança seja poderosa, é fácil fazer mau uso dela. Compartilharei algumas ideias para ajudar você a tirar o máximo proveito delas.

Evitando surpresas com métodos substituídos

Quando uma classe filha substitui um método de sua classe mãe, o comportamento pode mudar.

Por exemplo, se a classe principal Employee tiver um método calculate_pay() e a classe secundária Manager o substituir sem considerar todos os cenários, isso poderá produzir cálculos de pagamento incorretos.

A prática recomendada, nesse caso, é sempre testar minuciosamente os métodos substituídos e documentar o comportamento deles.

Escolha entre herança e composição

Sei que este artigo trata de herança, mas nem sempre essa é a abordagem correta. Às vezes, a composição, em que você cria classes combinando objetos em vez de estendê-los, pode ser mais adequada ao que você está fazendo.

Para distinguir as diferenças da maneira mais básica, você deve pensar assim:

  • A herança refere-se a relacionamentos "Is-a". Por exemplo, um Manager é um Employee.

  • Composição refere-se a relacionamentos "Has-a". Por exemplo, um Car tem um Engine.

Então, como você sabe quando a composição é a melhor abordagem a ser usada? Use a composição em quandoo relacionamento não for estritamente hierárquico e/ou quando você quiserreduzir o acoplamento rígido entre as classes.

Ou também poderíamos dizer que, enquanto a herança modela as relações, a composição se concentra na funcionalidade. Para ajudar, considere o seguinte:

  • Use a herança quando os objetos forem naturalmente hierárquicos. Por exemplo, Animal > Bird > Parrot.

  • Use a composição quando os objetos compartilharem funcionalidades, mas não estiverem relacionados. Por exemplo, um Printer e um Scanner usam um DeviceManager.

Evite cadeias de herança profundas

Cadeias de herança profundas (muitos níveis de relacionamentos pai-filho) podem dificultar a leitura e a manutenção do seu código. Isso é um problema porquealterações em uma classe principal podem afetar involuntariamente muitas classes secundárias. Além disso, a d ebuggingse torna complexa à medida que o comportamento se espalha por vários níveis.

A prática recomendada nesse caso é kmanter as hierarquias rasas. Além disso,considere usar a composição (como mencionei anteriormente) ou dividir uma cadeia em hierarquias separadas se você achar que ela está se tornando muito profunda.

Conclusão

A herança é um dos principais pilares da programação orientada a objetos que permite que desenvolvedores como você criem códigos reutilizáveis, modulares e dimensionáveis. Se você conseguir dominar a herança, será fácil simplificar sistemas complexos.

Uma boa maneira de aprofundar seu conhecimento é tentar criar estruturas de herança em seus projetos. Comece de forma simples e depois experimente hierarquias mais complexas para ver como elas funcionam na prática.

Se quiser se aprofundar ainda mais, você pode conferir nosso curso Conceitos do Paradigma de Programação para entender melhor a herança e outras ideias. Nosso plano de carreira de Desenvolvedor Python também é um bom recurso que oferece um caminho abrangente para o desenvolvimento de habilidades avançadas de programação que equiparão você para o desenvolvimento de software.


Samuel Shaibu's photo
Author
Samuel Shaibu
LinkedIn

Profissional experiente em dados e escritor que tem paixão por capacitar aspirantes a especialistas no espaço de dados.

Perguntas frequentes sobre herança em Python

O que é herança em Python?

A herança em Python é um mecanismo que permite que uma classe herde atributos e métodos de outra classe, promovendo a reutilização do código e estruturas de classes hierárquicas.

Como funciona a herança Python?

A herança Python funciona definindo uma nova classe que herda atributos e métodos de uma classe existente, permitindo estruturas de classes hierárquicas.

Quais são os tipos de herança em Python?

O Python oferece suporte a vários tipos de herança, incluindo herança única, múltipla, multinível e hierárquica.

Qual é a diferença entre herança única e múltipla em Python?

A herança única envolve uma classe principal, enquanto a herança múltipla permite que uma classe herde de várias classes principais.

Como você implementa a herança em Python?

Implemente a herança definindo uma nova classe que especifique uma classe pai em sua definição, usando a sintaxe class ChildClass(ParentClass):.

Quais são os benefícios de usar a herança em Python?

A herança promove a reutilização do código, permite a criação de estruturas de classes hierárquicas e oferece suporte ao polimorfismo, o que torna o código mais flexível e passível de manutenção.

Como você implementa a herança múltipla em Python?

A herança múltipla é implementada pela definição de uma classe que herda de mais de uma classe base, com a resolução de métodos de manipulação do Python usando a ordem de resolução de métodos (MRO).

Temas
Relacionado
Data Skills

blog

6 práticas recomendadas de Python para um código melhor

Descubra as práticas recomendadas de codificação Python para escrever os melhores scripts Python da categoria.
Javier Canales Luna's photo

Javier Canales Luna

13 min

5 Python Challenges

blog

5 desafios Python para desenvolver suas habilidades

Aumente o nível de suas habilidades em Python com estes cinco desafios de codificação em Python. Faça um teste para ver se você consegue completar um em uma semana!
DataCamp Team's photo

DataCamp Team

5 min

blog

Mais de 60 projetos Python para todos os níveis de conhecimento

60 ideias de projetos de ciência de dados que os cientistas de dados podem usar para criar um portfólio sólido, independentemente de sua especialização.
Bekhruz Tuychiev's photo

Bekhruz Tuychiev

16 min

tutorial

Programação orientada a objetos em Python (OOP): Tutorial

Aborde os fundamentos da programação orientada a objetos (OOP) em Python: explore classes, objetos, métodos de instância, atributos e muito mais!
Théo Vanderheyden's photo

Théo Vanderheyden

12 min

tutorial

Tutorial de Python

Em Python, tudo é objeto. Números, cadeias de caracteres (strings), DataFrames, e até mesmo funções são objetos. Especificamente, qualquer coisa que você usa no Python tem uma classe, um modelo associado por trás.
DataCamp Team's photo

DataCamp Team

3 min

tutorial

21 ferramentas essenciais do Python

Aprenda sobre as ferramentas Python essenciais para o desenvolvimento de software, raspagem e desenvolvimento da Web, análise e visualização de dados e aprendizado de máquina.
Abid Ali Awan's photo

Abid Ali Awan

6 min

Ver maisVer mais