Použití funkce super() v Python Classes

Photo of author

By etechblogcz

Základní aspekty

  • Funkce super() v Pythonu umožňuje volat metody nadřazené třídy z podřízené, což zjednodušuje implementaci dědičnosti a modifikaci metod.
  • Funkce super() je úzce spjata s pořadím vyhledávání metod (MRO) v Pythonu, které specifikuje, v jakém pořadí se prohledávají nadřazené třídy, hledají-li se metody nebo atributy.
  • Běžnou praxí je používání super() v konstruktorech tříd pro inicializaci společných atributů v nadřazené třídě a specifických v podřízené. Opomenutí super() může vést k nechtěným důsledkům, jako je absence inicializace atributů.

Jedním ze stěžejních rysů Pythonu je jeho OOP paradigma, které se dá využít k modelování reálných entit a jejich vzájemných vazeb.

Při práci s třídami Pythonu se často setkáte s dědičností a potřebou modifikovat atributy nebo metody nadřazené třídy. Python nabízí funkci super(), která umožňuje volat metody nadřazené třídy z třídy podřízené.

Co je super() a proč ji potřebujeme?

Pomocí dědičnosti je možné vytvořit novou třídu v Pythonu, která přebírá vlastnosti existující třídy. Rovněž je možné přepsat metody nadřazené třídy v podřízené, a tím poskytnout alternativní implementaci. Často je však výhodné využít novou funkci jako rozšíření stávající, nikoli jako její náhradu. Právě v takových případech se funkce super() stává neocenitelnou.

Funkci super() lze použít k přístupu k atributům nadřazené třídy a k vyvolání jejích metod. super() je klíčová pro objektově orientované programování, neboť usnadňuje implementaci dědičnosti a modifikaci metod.

Jak super() funguje?

Interně je super() úzce spjata s pořadím vyhledávání metod (MRO) v Pythonu, které je založeno na algoritmu C3 linearizace.

Princip fungování super() je následující:

  • Identifikace aktuální třídy a instance: Když je uvnitř metody podřízené třídy zavolána super(), Python automaticky určí aktuální třídu (třídu, která obsahuje metodu volající super()) a instanci této třídy (tedy self).
  • Identifikace nadřazené třídy: super() přebírá dva argumenty – aktuální třídu a instanci – které se obvykle explicitně nepředávají. Tyto informace se využívají k určení nadřazené třídy, na kterou se deleguje volání metody. Tohoto dosahuje zkoumáním hierarchie tříd a MRO.
  • Volání metody v nadřazené třídě: Jakmile je nadřazená třída identifikována, super() umožňuje volat její metody, jako by byly volány přímo z podřízené třídy. Umožňuje to rozšiřovat nebo upravovat metody, přičemž lze využít původní implementaci z nadřazené třídy.

Použití super() v konstruktoru třídy

Použití super() v konstruktoru třídy je běžné, jelikož je často třeba inicializovat společné atributy v nadřazené třídě a specifické atributy v podřízené třídě.

Jako demonstraci definujme třídu Otec, ze které bude dědit třída Syn:

 class Otec:
    def __init__(self, jmeno, prijmeni):
        self.jmeno = jmeno
        self.prijmeni = prijmeni

class Syn(Otec):
    def __init__(self, jmeno, prijmeni, vek, konicek):
        
        super().__init__(jmeno, prijmeni)

        self.vek = vek
        self.konicek = konicek

    def ziskej_info(self):
        return f"Jméno syna: {self.jmeno} {self.prijmeni}, 
Věk syna: {self.vek}, Koníček syna: {self.konicek}" son = Syn("Pius", "Effiong", 25, "Hra na kytaru") print(son.ziskej_info())

Uvnitř konstruktoru třídy Syn volání super().__init__() vyvolá konstruktor třídy Otec a předá mu jméno a příjmení jako parametry. To zajistí, že třída Otec bude moci správně nastavit atributy jména, a to i v objektu třídy Syn.

Pokud v konstruktoru třídy nezavoláte super(), konstruktor nadřazené třídy se neprovede. To může vést k nechtěným důsledkům, jako je absence inicializace atributů nebo neúplné nastavení stavu nadřazené třídy:

...
class Syn(Otec):
    def __init__(self, jmeno, prijmeni, vek, konicek):
        self.vek = vek
        self.konicek = konicek
...

Pokud se nyní pokusíte zavolat metodu ziskej_info, dojde k vyvolání výjimky AttributeError, protože atributy self.jmeno a self.prijmeni nebyly inicializovány.

Použití super() v metodách třídy

super() lze použít i v jiných metodách, nejen v konstruktorech. Umožňuje to rozšířit nebo modifikovat chování metody nadřazené třídy.

 class Otec:
    def mluv(self):
        return "Zdravím, jsem Otec"

class Syn(Otec):
    def mluv(self):
        
        pozdrav_rodice = super().mluv()
        return f"Zdravím, jsem Syn\n{pozdrav_rodice}"

son = Syn()

pozdrav_syna = son.mluv()

print(pozdrav_syna)

Třída Syn dědí od třídy Otec a má svou vlastní metodu mluv. Metoda mluv ve třídě Syn používá super().mluv() pro volání metody mluv třídy Otec. To mu umožňuje zahrnout zprávu z nadřazené třídy a současně ji rozšířit o zprávu specifickou pro podřízenou třídu.

Opomenutí super() v metodě, která přepisuje jinou, vede k tomu, že se funkce z metody nadřazené třídy neprojeví. Výsledkem je úplná náhrada chování metody, což může způsobit chování, které nebylo zamýšleno.

Porozumění pořadí vyhledávání metod (MRO)

Pořadí vyhledávání metod (MRO) určuje, v jakém pořadí Python prohledává nadřazené třídy při přístupu k metodě nebo atributu. MRO pomáhá Pythonu rozhodnout, kterou metodu zavolat, když existuje více hierarchií dědičnosti.

 class Nigérie():
    def kultura(self):
        print("Kultura Nigérie")

class Afrika():
    def kultura(self):
        print("Kultura Afriky")

Co se stane, když vytvoříte instanci třídy Lagos a zavoláte metodu kultura:

  • Python začne hledat metodu kultura v samotné třídě Lagos. Pokud ji najde, zavolá ji. Pokud ne, přejde ke kroku dva.
  • Pokud metodu kultura nenajde v třídě Lagos, Python se podívá na základní třídy v pořadí, v jakém se objevují v definici třídy. V tomto případě Lagos dědí nejprve z Afriky a poté z Nigérie. Python tedy nejprve bude hledat metodu kultura v Africe.
  • Pokud nenajde metodu kultura v třídě Afrika, Python ji bude hledat v třídě Nigérie. Toto chování pokračuje, dokud nedosáhne konce hierarchie a vyvolá chybu, pokud nenajde metodu v žádné z nadřazených tříd.

Výstup ukazuje pořadí řešení metod pro Lagos, počínaje zleva doprava.

Běžná úskalí a osvědčené postupy

Při práci s super() existuje několik běžných úskalí, kterým je dobré se vyhnout.

  • Mějte na paměti pořadí vyhledávání metod, zvláště v situacích s vícenásobnou dědičností. Pokud potřebujete používat složitou vícenásobnou dědičnost, měli byste se seznámit s C3 linearizačním algoritmem, který Python používá pro určení MRO.
  • Vyhněte se cyklickým závislostem v hierarchii tříd, které mohou vést k nepředvídatelnému chování.
  • Dokumentujte svůj kód srozumitelně, zvláště když používáte super() ve složitých hierarchiích tříd. Zlepšíte tak srozumitelnost kódu pro ostatní vývojáře.

Používejte super() správně

Funkce super() v Pythonu je silným nástrojem při práci s dědičností a modifikací metod. Pochopení, jak super() funguje, a dodržování osvědčených postupů vám umožní vytvářet lépe udržovatelný a efektivnější kód ve vašich Python projektech.