Ao se aprofundar no mundo do Python, especialmente na programação orientada a objetos, você frequentemente se deparará com a função super()
. Essa função é uma parte vital da herança de classes e desempenha um papel crucial na inicialização de métodos herdados dentro de classes derivadas. Mas por que exatamente usamos a função super()
do Python, e como ela difere de chamar diretamente o método __init__
de uma classe base? Vamos explorar essas questões em detalhes.
O que a função super()
do Python faz?
Quando criamos uma classe derivada (“filha”) a partir de uma classe base (“pai”) em Python, os métodos e atributos da classe derivada acabam por sobrescrever os métodos da classe base de mesmo nome. No exemplo abaixo, note que a classe Derivada
não consegue acessar o método Base.bar()
, porque ela possui uma nova implementação do mesmo método:
class Base():
def foo(self):
print('Base chamou método foo')
def bar(self):
print('Base chamou método bar')
class Derivada(Base):
def bar(self):
print('Derivada chamou método bar')
d = Derivada()
d.foo() # Output: Base chamou método foo
d.bar() # Output: Derivada chamou método bar
Este é o comportamento esperado, afinal uma classe derivada pode muitas vezes querer usar uma implementação diferente de um método da classe base. Dito isso, e se quiséssemos acessar o método Base.bar()
a partir da instância de Derivada()
? É aí que entra a função super()
do Python: ela nos dá uma referência à classe imediatamente acima da classe em que é chamada e, por consequência, concede acesso direto a todos os seus métodos:
class Base():
def foo(self):
print('Base chamou método foo')
def bar(self):
print('Base chamou método bar')
class Derivada(Base):
def bar(self):
print('Derivada chamou método bar')
def bar_base(self):
return super().bar()
d = Derivada()
d.foo() # Output: Base chamou método foo
d.bar() # Output: Derivada chamou método bar
d.bar_base() # Output: Base chamou método bar
No exemplo acima, o método Derivada.bar_base
usa a função super()
para acessar o método Base.bar
.
O Papel da Função super()
do Python na Herança de Classes
Como vimos anteriormente, a função super()
do Python é usada para dar acesso a métodos de uma classe herdada sem nomear explicitamente a classe base. Isso é particularmente útil porque, se trocarmos a classe base, o código será capaz de automaticamente acessar os métodos da nova base.
Considere o seguinte exemplo:
class Base:
def __init__(self):
print("Base inicializada")
class Derivada1(Base):
def __init__(self):
Base.__init__(self)
class Derivada2(Base):
def __init__(self):
super().__init__()
d1 = Derivada1() # output: Base inicializada
d2 = Derivada2() # output: Base inicializada
Quando instanciamos objetos das classes Derivada1()
e Derivada2()
, ambos imprimirão “Base inicializada”, uma vez que ambos acessam e executam o método Base.__init__()
dentro do seu próprio método __init__()
. No entanto, como veremos a seguir, a maneira como eles acessam o método Base.__init__()
é diferente.
Em
, chamamos diretamente Derivada1
Base.__init__(self)
, o que é simples, mas não flexível. Se decidirmos mudar a classe base ou introduzir herança múltipla, precisaríamos atualizar a referência à classe base em todos os lugares onde ela é chamada explicitamente.
Por outro lado,
usa Derivada
2super().__init__()
, que não requer que o nome da classe base seja explicitamente declarado. Isso não é apenas mais limpo, mas também mais fácil de manter, especialmente ao lidar com herança múltipla.
A Função super()
com Hierarquias de Classes Complexas
O verdadeiro poder de super()
é revelado em estruturas de herança complexas. Ele usa a ordem de resolução de métodos (MRO, sigla do inglês method resolution order) para determinar a próxima classe a procurar pelo método __init__
. Isso garante que todos os métodos __init__
na hierarquia de herança sejam chamados na ordem e quantidade corretos, o que é essencial para inicializar todos os aspectos de um objeto adequadamente.
Um Exemplo Prático
Veja o que acontece com uma classe contendo herança múltipla — neste exemplo, a classe User
representada pelo diagrama abaixo:
Abaixo, a mesma estrutura reproduzida em código:
class Base:
def __init__(self):
print("Base inicializada")
class ChildA(Base):
def __init__(self):
print("ChildA inicializada")
super().__init__()
class ChildB(Base):
def __init__(self):
print("ChildB inicializada")
super().__init__()
class User(ChildA, ChildB):
def __init__(self):
print("User inicializada")
super().__init__()
user = User()
Quando criamos uma instância de User
, as respectivas chamadas a super().__init__
são feitas respeitando a hierarquia de classes. A saída é:
User inicializada
ChildA inicializada
ChildB inicializada
Base inicializada
Isso demonstra que o método super().__init__
dentro da classe User
chama corretamente os métodos __init__
de ChildA
, ChildB
e Base
na ordem certa. Além disso, embora ambos ChildA
e ChildB
acabem chamando Base.__init__
através de super()
, veja que este método executa apenas uma vez. Isso tudo é fruto da praticidade da função super()
do Python!
Resumo
Em resumo, a função super()
do Python é uma função que permite uma maneira limpa e fácil de manter para chamar métodos de classes pai no Python, especialmente em hierarquias de herança complexas. Ela aproveita a ordem de resolução de métodos para garantir que todas as classes pai sejam inicializadas corretamente. Com o Python 3, super()
tornou-se ainda mais amigável, permitindo chamadas sem argumentos. Lembre-se de evitar usar super(self.__class__, self).__init__()
pois isso pode levar a problemas de recursão. Em vez disso, aproveite a simplicidade e o poder de super()
para escrever códigos de Python orientados a objetos mais fáceis de manter e livres de erros.
Cursos de programação gratuitos com certificado
Aprenda a programar e desenvolva soluções para o seu trabalho com Python para alcançar novas oportunidades profissionais. Aqui na Asimov você encontra:
- Conteúdos gratuitos
- Projetos práticos
- Certificados
- +20 mil alunos e comunidade exclusiva
- Materiais didáticos e download de código
Comentários