Contents

Utilizar a função super() em classes Python

Key Takeaways

A utilização da função super() em Python facilita a invocação de métodos numa subclasse acedendo à sua superclasse, melhorando assim o processo de implementação da herança e das substituições de métodos de uma forma elegante.

A função super() tem uma ligação significativa com a Ordem de Resolução de Métodos (MRO) em Python, que governa a sequência em que as classes ancestrais são escrutinadas quanto a métodos e propriedades durante a resolução de métodos.

A utilização de super() no construtor de uma classe é uma abordagem habitual para estabelecer características partilhadas na classe-mãe e definir traços mais particulares na classe subordinada. A não implementação de super() pode resultar em resultados indesejados, incluindo a omissão da inicialização de atributos.

Um dos aspectos fundamentais da linguagem de programação Python é o seu paradigma de Programação Orientada para Objectos (POO), que permite a criação de modelos que representam objectos da vida real e as suas interligações de forma estruturada.

No contexto da programação com Python, é comum utilizar a herança para modificar ou substituir características ou funções da entidade-mãe de uma classe. A linguagem oferece um mecanismo conveniente conhecido como a função “super()”, que permite efetuar chamadas aos métodos da classe superior a partir da classe subordinada.

O que é super() e porque é que precisa dela?

A herança de uma classe existente permite a criação de uma nova classe com atributos e comportamentos semelhantes através do processo de herança. Além disso, pode-se optar por substituir métodos específicos dentro da subclasse para fornecer sua própria implementação exclusiva. Embora esta nova funcionalidade possa ser desejável, há casos em que é preferível utilizar simultaneamente as funcionalidades originais e as recém-implementadas. Nesses casos, a utilização da função super() permite a integração de ambos os conjuntos de capacidades na subclasse.

Para utilizar os atributos e métodos de uma superclasse na programação orientada para objectos, pode utilizar-se a função super() . Esta função desempenha um papel crucial, pois facilita a implementação da herança e da substituição de métodos, que são conceitos fundamentais na programação orientada para objectos.

Como é que super() funciona?

Internamente, o comportamento da função super() está interligado com o conceito de Ordem de Resolução de Métodos (MRO), um mecanismo que é determinado pelo algoritmo de linearização C3 em Python.

Eis como funciona a função super():

Ao chamar super() dentro de um método de uma subclasse em Python, a linguagem identifica automaticamente tanto a classe atual, que contém o método invocado, como a instância dessa classe representada por self .

⭐ Determinar a superclasse : super() recebe dois argumentos - a classe atual e a instância - que não é necessário passar explicitamente. Ele usa essas informações para determinar a superclasse para delegar a chamada do método. Ele faz isso examinando a hierarquia de classes e o MRO.

Uma vez identificada uma superclasse, a função super() permite a invocação dos seus métodos como se estivessem a ser chamados diretamente da subclasse. Esta capacidade facilita tanto a extensão como a substituição de métodos da superclasse, utilizando sempre a implementação original fornecida pela superclasse.

Usando super() em um construtor de classe

Incorporar o uso do método super() nos construtores de classes é uma abordagem predominante, pois permite a inicialização de propriedades compartilhadas entre classes irmãs, ao mesmo tempo em que permite características únicas em cada descendência individual.

Para ilustrar o conceito de herança na programação orientada a objectos utilizando Python, podemos criar uma classe “Pai” que serve de classe base para outra classe chamada “Filho”. A classe “Filho” herdará propriedades e métodos da sua classe-mãe, a classe “Pai”, demonstrando assim como a herança permite a reutilização e a modularidade do código.

 class Father:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

class Son(Father):
    def __init__(self, first_name, last_name, age, hobby):
        # Call the parent class constructor (Father)
        super().__init__(first_name, last_name)

        self.age = age
        self.hobby = hobby

    def get_info(self):
        return f"Son's Name: {self.first_name} {self.last_name}, \
Son's Age: {self.age}, Son's Hobby: {self.hobby}"

# Create an instance of the Son class
son = Son("Pius", "Effiong", 25, "Playing Guitar")

# Access attributes
print(son.get_info())

Dentro dos limites do construtor da classe Filho , o imperativo de invocar os auspícios da sua classe-mãe é executado através do ato prodigioso de invocar a função super() . Esta ação fundamental desencadeia a iniciação do construtor da classe paterna Pai , oferecendo dois estimados qualificadores primordiais sob a forma das variáveis nome_primeiro e nome_último . Desta forma, a classe Pai mantém a capacidade de obter e atribuir com exatidão estas designações nominais, não obstante a instanciação de um Filho .

/pt/images/using-super-in-class-constructor.jpg

Nos casos em que o método super() não é invocado dentro da construção

 ...
class Son(Father):
    def __init__(self, first_name, last_name, age, hobby):
        self.age = age
        self.hobby = hobby
...

Caso tente invocar o método get_info neste momento, isso resultará na geração de um AttributeError , uma vez que o self. first_name e self.last_name ainda não foram inicializados.

/pt/images/trying-to-use-parent-attributes-without-super.jpg

Usando super() em métodos de classe

Pode-se empregar a utilização da função super() não apenas dentro dos limites dos procedimentos do construtor, mas também em uma série diversificada de métodos para aumentar ou substituir a funcionalidade da abordagem metodológica da classe pai.

 class Father:
    def speak(self):
        return "Hello from Father"

class Son(Father):
    def speak(self):
        # Call the parent class's speak method using super()
        parent_greeting = super().speak()
        return f"Hello from Son\n{parent_greeting}"

# Create an instance of the Son class
son = Son()

# Call the speak method of the Son class
son_greeting = son.speak()

print(son_greeting)

A classe Son em Python foi concebida para herdar propriedades e métodos da sua classe-mãe ou Father , incluindo o método speak() . Ao utilizar a função super().speak() dentro do método speak() da classe Son , pode efetivamente invocar o método original speak() da classe Father , ao mesmo tempo que incorpora quaisquer informações ou mensagens adicionais exclusivas da classe Son .

/pt/images/using-super-in-class-mthod.jpg

Nos casos em que um método substitui outro sem utilizar a função super() fornecida, a funcionalidade pretendida da classe-mãe não será executada. Consequentemente, isto pode resultar num comportamento não intencional devido a uma metodologia totalmente nova e distinta que substitui a funcionalidade original.

Compreender a Ordem de Resolução de Métodos

A Ordem de Resolução de Métodos (MRO) refere-se à hierarquia de precedência que determina a ordem pela qual Python irá procurar métodos e atributos ao lidar com estruturas de classes complexas que envolvem herança múltipla. Este mecanismo assegura que o método apropriado é chamado com base nas opções disponíveis nas respectivas classes-mãe, resolvendo assim quaisquer potenciais ambiguidades resultantes de definições sobrepostas.

 class Nigeria():
    def culture(self):
        print("Nigeria's culture")

class Africa():
   def culture(self):
       print("Africa's culture") 

Ao instanciar a classe Lagos e invocar o seu método de cultura, ocorrem vários eventos que contribuem para a funcionalidade geral do objeto. A execução do código passa por várias fases que envolvem a manipulação de dados, a obtenção de informações de fontes externas e a aplicação de vários algoritmos para analisar aspectos culturais com base em parâmetros de entrada fornecidos pelo utilizador ou em valores predefinidos definidos no construtor. O resultado final é uma avaliação exaustiva dos atributos culturais da cidade especificada, representada pela classe Lagos.

Python procura primeiro o método culture dentro da própria classe Lagos . Se este método for descoberto e estiver disponível, o algoritmo invoca-o. No entanto, se esse método não existir ou não puder ser acedido, o processo avança para a fase seguinte.

Se a classe Lagos não tiver um método culture() , Python irá procurar o método nas classes base da hierarquia de classes, começando pelas classes listadas depois dos dois pontos ( : ), que são herdadas pela classe Lagos . Neste exemplo, a classe Lagos é definida para herdar primeiro da classe África e, em segundo lugar, da classe Nigéria . Por conseguinte, se a classe Lagos não contiver a sua própria implementação do método culture() , Python tentará localizar o método na classe África antes de procurar na classe Nigéria .

A procura de um método específico de uma cultura numa determinada hierarquia de classes prossegue através de uma série de classes aninhadas, com cada classe subsequente a ser examinada no caso de o método desejado ser aí encontrado. No caso de o método não ser descoberto no nível inicial de investigação, nomeadamente a classe africana, Python muda o seu foco para a classe mais alta seguinte na estrutura hierárquica, que neste caso corresponde à classe nigeriana. Este processo repete-se à medida que se desce na árvore de classes, estendendo-se da mais geral para a menos específica, até ao momento em que o método procurado não pode ser localizado em nenhuma das superclasses encontradas. Nesse momento, é levantada uma exceção para indicar o resultado infrutífero da pesquisa.

/pt/images/the-mothod-resolution-order.jpg

A ordem de resolução de métodos (MRO) para Lagos é apresentada de forma linear, começando da esquerda e progredindo para a direita.

Armadilhas comuns e boas práticas

Ao utilizar a função super() , é importante estar ciente de alguns potenciais erros para garantir uma experiência suave e eficaz.

⭐ Tenha em atenção a ordem de resolução do método, especialmente em cenários de herança múltipla. Se precisar de usar herança múltipla complexa, deve estar familiarizado com o algoritmo de Linearização C3 que o Python usa para determinar a MRO.

As dependências circulares dentro de uma hierarquia de classes devem ser evitadas, uma vez que podem resultar num comportamento errático e difícil de antecipar.

Ao trabalhar com hierarquias de classes complexas que envolvem o uso da função super() , é crucial manter uma base de código clara e bem documentada. Isso não apenas garante a funcionalidade adequada, mas também promove a compreensão entre colegas desenvolvedores que podem precisar trabalhar ou modificar o código no futuro. Ao fornecer comentários concisos e informativos, pode melhorar a legibilidade e a manutenção do seu projeto, facilitando a colaboração e reduzindo potenciais erros ou confusões.

Utilizar super() da forma correcta

A utilização da função super() em Python representa um aspeto indispensável do seu paradigma de programação orientado para objectos, particularmente em relação à facilitação de heranças e substituições de métodos. A compreensão dos meandros associados a esta entidade funcional e a adesão às melhores práticas estabelecidas permite o desenvolvimento de aplicações de software mais duradouras e com mais recursos dentro do ecossistema Python.