8.1 Polimorfismo
Polimorfismo é uma das principais conceitos da programação orientada a objetos (POO), que permite que objetos de diferentes classes usem uma única interface.
Em Python, o polimorfismo é alcançado através de tipagem dinâmica e herança. Um aspecto importante do polimorfismo é a sobrecarga de métodos e a substituição de métodos da classe base por métodos da classe derivada.
Principais conceitos do polimorfismo:
Interface única para diferentes objetos:
O polimorfismo permite o uso de uma única interface para objetos de diferentes classes, que podem ter diferentes implementações de métodos.
Tipagem dinâmica:
Em Python, o tipo da variável é determinado em tempo de execução, o que permite que a função trabalhe com objetos de qualquer classe, se eles suportarem a interface necessária.
Herança:
Permite criar hierarquias de classes, onde subclasses herdam propriedades e métodos da classe base, mas podem sobrescrevê-los para sua funcionalidade específica.
Exemplos de polimorfismo:
Um exemplo simples de polimorfismo: todas as três classes têm um método chamado move(). Isso significa que podemos escrever um código que trabalhe com esses objetos ao mesmo tempo.
class Car:
def move(self):
pass
class Human:
def move(self):
pass
class Bird:
def move(self):
print("Car!")
car = Car()
human = Human()
bird = Bird()
for it in [car, human, bird]:
it.move()
Neste caso, a "interface comum" é o nome do método, ou sua assinatura, se o método tiver parâmetros.
8.2 Sobrescrição de métodos
O polimorfismo é frequentemente usado junto com herança para criar hierarquias de classes, onde a classe base define uma interface comum, e as subclasses implementam detalhes específicos.
class Employee:
def get_salary(self):
return 1000
class FullTimeEmployee(Employee):
def get_salary(self):
return 5000
class PartTimeEmployee(Employee):
def get_salary(self):
return 3000
class Intern(Employee):
pass
def print_salary(employee):
print(employee.get_salary())
employees = [FullTimeEmployee(), PartTimeEmployee(), Intern()]
for employee in employees:
print_salary(employee)
Este exemplo é semelhante ao anterior, mas há uma diferença importante — nossas classes não são obrigadas a ter o método get_salary, já que ele sempre existe na classe base. Agora, a "interface comum" de todas as classes não é apenas o método get_salary(), mas sim a classe Employee com todos os seus métodos e atributos.
8.3 Chamando métodos da classe derivada
Preste atenção ao código abaixo:
Nós chamamos o método print_salary(), que só existe na classe base Employee, e este já na classe base chama outro método da classe base — get_salary(). Então quais salários serão exibidos na tela?
class Employee:
def print_salary(self):
salary = self.get_salary()
print(salary)
def get_salary(self):
return 1000
class FullTimeEmployee(Employee):
def get_salary(self):
return 5000
class PartTimeEmployee(Employee):
def get_salary(self):
return 3000
class Intern(Employee):
pass
employees = [FullTimeEmployee(), PartTimeEmployee(), Intern()]
for employee in employees:
employee.print_salary()
Importante! Leia o que está escrito abaixo — é importante.
Se chamarmos o método print_salary(), por exemplo, da classe FullTimeEmployee, então o método da classe base será chamado, pois tal método não está declarado na própria classe.
Mas este método print_salary() chamará o método get_salary() do objeto self, que tem tipo FullTimeEmployee, e não Employee. Portanto, será chamado o método get_salary() da classe FullTimeEmployee!
Além disso, a classe FullTimeEmployee pode chamar o método get_salary() da classe base para suas necessidades. Por exemplo, queremos calcular o salário em porcentagem do salário base:
class FullTimeEmployee(Employee):
def get_salary(self):
base_salary = super().get_salary()
return base_salary * 5
# 500%
class PartTimeEmployee(Employee):
def get_salary(self):
base_salary = super().get_salary()
return base_salary * 3 # 300%
8.4 Sobrecarga de métodos
Sobrecarga de métodos (method overloading) em Python é a habilidade de criar vários métodos com o mesmo nome, mas com diferentes parâmetros. No entanto, sobrecarga de métodos não é suportada em Python na sua forma pura como em outras linguagens (por exemplo, C++ ou Java).
Em Python, é possível imitar a sobrecarga de métodos através do uso de argumentos padrão, *args e **kwargs.
Exemplos de sobrecarga de métodos
Podemos implementar uma função que terá comportamento diferente dependendo do número de argumentos passados. Isso pode ser feito de várias formas, por exemplo, assim:
class Example:
def display(self, a=None, b=None):
if a is not None and b is not None: print(a, b)
elif a is not None: print(a)
else: print("Sem argumentos")
obj = Example()
obj.display(1, 2) # Output: 1 2
obj.display(1) # Output: 1
obj.display() # Output: Sem argumentos
Também podemos considerar o tipo dos dados passados — basta fazer uma verificação de tipo:
class Example:
def mod(self, a, b):
if type(a) == int and type(b) == int: print(a % b)
elif type(a) == float or type(b) == float: print(round(a / b))
else: print("Instrução de ajuda: a e b devem ser int ou float")
obj = Example()
obj.mod(5, 2) # Output: 1
obj.mod(5.0, 2) # Output: 2
obj.mod("5", 2) # Output: Instrução de ajuda: a e b devem ser int ou float
Se os tipos de dados passados forem incorretos, podemos exibir uma instrução ou até mesmo um link para a documentação na internet — isso também é uma solução muito popular.
GO TO FULL VERSION