Herança em Python
A herança permite criar uma nova classe com base em uma já existente. Juntamente com a encapsulação, a herança é um dos pilares da programação orientada a objetos.
Os principais conceitos na herança são subclasse e superclasse. A subclasse herda todos os atributos e métodos públicos da superclasse. A superclasse também é chamada de classe base ou classe pai, enquanto a subclasse é conhecida como classe derivada ou classe filha.
O seguinte é o exemplo da sintaxe para herança de classes:
class Subclasse(Superclasse):
métodos_subclasse
Por exemplo, temos uma classe Person
, que representa uma pessoa:
class Person:
def __init__(self, name):
self.__name = name # nome da pessoa
@property
def name(self):
return self.__name
def display_info(self):
print(f"Name: {self.__name}")
Agora, vamos supor que precisamos de uma classe Employee
para representar um funcionário de uma empresa. Podemos criar essa classe do zero:
class Employee:
def __init__(self, name):
self.__name = name # nome do funcionário
@property
def name(self):
return self.__name
def display_info(self):
print(f"Name: {self.__name}")
def work(self):
print(f"{self.name} works")
No entanto, a classe Employee
possui os mesmos atributos e métodos que a classe Person, pois um funcionário é, afinal, uma pessoa. O código da classe Employee
repete a funcionalidade da classe Person
, exceto pelo método work
. Para evitar essa duplicação de código, podemos aplicar a herança.
Assim, vamos herdar a classe Employee
da classe Person
:
class Person:
def __init__(self, name):
self.__name = name # nome da pessoa
@property
def name(self):
return self.__name
def display_info(self):
print(f"Name: {self.__name}")
class Employee(Person):
def work(self):
print(f"{self.name} works")
tom = Employee("Tom")
print(tom.name) # Tom
tom.display_info() # Name: Tom
tom.work() # Tom works
A classe Employee
herda totalmente a funcionalidade da classe Person
e adiciona seu próprio método work
. Dessa forma, ao criar um objeto da classe Employee
, podemos utilizar o construtor herdado de Person
:
tom = Employee("Tom")
Também podemos acessar os atributos e métodos herdados:
print(tom.name) # Tom
tom.display_info() # Name: Tom
Vale lembrar que Employee
não pode acessar atributos privados da superclasse, como o atributo __name
. Por exemplo, não é possível usar self.__name
diretamente no método work
:
def work(self):
print(f"{self.__name} works") # Erro!
Herança Múltipla
Uma característica distintiva do Python é o suporte à herança múltipla, onde uma classe pode herdar de várias classes:
# Classe Employee
class Employee:
def work(self):
print("Employee works")
# Classe Student
class Student:
def study(self):
print("Student studies")
class WorkingStudent(Employee, Student): # Herança de Employee e Student
pass
tom = WorkingStudent()
tom.work() # Employee works
tom.study() # Student studies
Neste exemplo, WorkingStudent
representa um estudante que trabalha, herdando as funcionalidades de Employee
e Student
. Assim, podemos chamar métodos de ambas as classes:
tom = WorkingStudent()
tom.work() # Employee works
tom.study() # Student studies
Os métodos herdados podem ter funcionalidade mais complexa, como mostrado a seguir:
class Employee:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
def work(self):
print(f"{self.name} works")
class Student:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
def study(self):
print(f"{self.name} studies")
class WorkingStudent(Employee, Student):
pass
tom = WorkingStudent("Tom")
tom.work() # Tom works
tom.study() # Tom studies
Embora a herança múltipla seja prática, ela pode gerar confusão caso ambas as classes possuam métodos ou atributos com os mesmos nomes. Veja o exemplo:
class Employee:
def do(self):
print("Employee works")
class Student:
def do(self):
print("Student studies")
class WorkingStudent(Employee, Student):
pass
tom = WorkingStudent()
tom.do() # Qual método será usado?
No exemplo, ambas as classes Employee
e Student
possuem um método do
, cada um com um propósito distinto. Quando definimos WorkingStudent
com Employee
primeiro, o método do
de Employee
é o que será chamado.
Para alterar a preferência, basta trocar a ordem das classes:
class WorkingStudent(Student, Employee)
É possível verificar a ordem de aplicação da funcionalidade das classes bases com o atributo __mro__
ou o método mro()
:
print(WorkingStudent.__mro__)
print(WorkingStudent.mro())
Estes métodos retorna uma listam de classes na ordem em que os métodos são procurados pelo Python.