Dědičnost v Pythonu

Zde je příklad třídy Zvire, na které si můžeme ukázat principy dědičnosti:

class Zvire:
    def __init__(self, jmeno, zvuk, oblibene_jidlo):
        self.jmeno = jmeno
        self.zvuk = zvuk
        self.oblibene_jidlo = oblibene_jidlo
        self.spokojenost = 5
        self.hlad = 5

    def udelej_zvuk(self):
        if self.hlad < 4:
            print(f"{self.zvuk.upper()}")
        else:
            print(f"{self.zvuk}")

    def snez(self,jidlo):
        if jidlo == self.oblibene_jidlo:
            print(f"Mnam! {jidlo} mi moc chutna!")
            self.hlad += 2
            self.spokojenost += 1
        else:
            print(f"Dobrá, sním {jidlo}...")
            self.hlad += 1

    def hraj_si(self, hracka):
        self.hlad -= 1
        self.spokojenost += 1

Třída Zvire má atributy:

A metody:

Je to takový velmi obecný popis toho, co zvířátko je a co umí.

Naše třída Kocicka z minula je ale speciální - takové kočičky sbírají hračky. Jak tedy bude vypadat, když má předka Zvire?

class Kocicka(Zvire):
    def __init__(self, jmeno, zvuk="Mnau", hracky=None):
        super().__init__(jmeno, zvuk)
        if hracky is None:
            self.hracky = []
        else:
            self.hracky = hracky


    

zavolání super().__init__(jmeno, zvuk) se stará o to, aby se do atributů Kočičky přidalo self.jmeno, self.zvuk, self.oblibene_jidlo, self.spokojenost a self.hlad

Spolu s tím Kocicka podědí i všechny metody třídy Zvire. A stejně tak to bude pro jakékoliv další potomky třídy Zvire

Díky tomu lze jednoduše vytvářet nové druhy zvířat a přidávat specifické chování, pojďmě to vyzkoušet na Pejskovi!

class Pejsek(Zvire):
    def __init__(self, jmeno, oblibene_jidlo, zvuk="Haf"):
        super().__init__(jmeno, zvuk, oblibene_jidlo)
        self.naucene_povely = []

    def hlidej(self):
        print(f"{self.jmeno} štěká a hlídá dům!")
        self.spokojenost += 1

    def nauc_se_povel(self, povel):
        povel = povel.lower()
        if povel not in self.naucene_povely:
            while True:
                nahoda = random.randint(0,6)
                if nahoda == 6:
                    self.naucene_povely.append(povel)
                    print(f"{self.jmeno} už umí {povel}")

                    break
                elif nahoda == 1:
                    print(f"{self.jmeno} se tváří, že vůbec nechápe, co má dělat.")
                else:
                    print(f"{self.jmeno} jen kouká. Musí ještě trénovat")


Třída Pejsek dědí od třídy Zvire a přidává nové chování, které je typické pro psy - hlídání a učení se povelům.

Co když ale potřebujeme, aby metoda třídy dělala něco trochu jiného, než dělá metoda jejího předka? Jednoduše ji přepíšeme – vytvoříme novou metodu se stejným jménem. Tím se původní verze nahradí a při volání se použije ta nová.

class Myska(Zvire):
    def __init__(self, jmeno, oblibene_jidlo, zvuk="Mnau", hracky=None):
        super().__init__(jmeno, zvuk, oblibene_jidlo)

    def udelej_zvuk(self):
        print(f"Myška {self.jmeno} je potichu jako myška.")

    

Protože myšky nedělají zvuk, metoda třídy Myska nám jen oznámí, že je zkrátka potichoučku. (Že myšky fakt nejsou potichu můžeme probrat na pivu po hodině)

Co kdybychom ale chtěly jen něco přidat k metodě předka? Od toho máme funkci super(). Zkusíme udělat kočky trochu podezřívavé.

class Kocicka(Zvire):
    def __init__(self, jmeno, oblibene_jidlo, zvuk="Mnau", hracky=None):
        super().__init__(jmeno, zvuk, oblibene_jidlo)
        if hracky is None:
            self.hracky = []
        else:
            self.hracky = hracky

    def snez(self,jidlo):
        print(f"Kočička {self.jmeno} na {jidlo} chvíli podezřívavě kouká a šťouchá pacinkou.")
        super().snez(jidlo)


micka = Kocicka("Bětka", "maso")
micka.snez("maso")


    

super() použijeme, když chceme metodu z předka zachovat, ale něco k ní přidat. Umožní nám zavolat původní verzi metody a doplnit ji o vlastní chování.

Polymorfismus

Programátoři nezavedli dědičnost jen proto, že jsou líní a nechtějí psát dvakrát stejný kód. To je sice dobrý důvod, ale nadtřídy mají jednu důležitější vlastnost. Když víš, že každá Kocicka nebo Pejseknebo jakákoli jiná podtřída je zvířátko, můžeš si udělat seznam zvířátek s tím, že pak bude jedno, jaká přesně zvířátka to jsou:


pejsek = Pejsek("Fík", "datle", "hauuu")
zvirata= [Kocicka("Bětka", "Mňa", "chleba"), pejsek, Myska("Remy", "sýr")]

for zvire in zvirata:
    zvire.snez("banán")
    print()