5

Třídy a objekty

Třídy a objekty jsou způsob, jak strukturovat program. Tato kapitola nás uvede do světa objektově orientovaného programování.

Dalším konceptem, se kterým se v tomto kurzu seznámíme, jsou objekty (objects). Na objektech je založeno objektově orientované programování (Object-oriented programming - OOP), tedy princip psaní programů, ve kterém jsou bloky kódu poskládané do tříd a objektů.

Objekty mají často reprezentovat nějaké entity v realitě. Pokud bychom například vyvíjeli administrativní software pro firmy, vytvoříme tam objekty reprezentující zaměstnance, pracoviště, firemní automobily atd. U zásilkové společnosti bychom vytvářeli objekty, které reprezentují balíky, řidiče atd.

Objekty a třídy

Na začátku si musíme vytvořit třídu (class). Vztah mezi třídou a objekty si můžeme představit na příkladu formulářů. Třída je prázdný formulář - obsahuje kolonky, které by měly být vyplněny. Objekt je pak vyplněný formulář, který už má v sobě nějaká konkrétní data. Podobně jako formulářů můžeme vyplnit více, může na základě jedné třídy vzniknout několik objektů. Objekty jsou vzájemně nezávislé, takže práce s jedním objektem neovlivňuje ostatní. Analogicky, pokud upravujeme jeden formulář, nijak tím neměníme ostatní.

Třídy mají dvě důležité charakteristiky - mají atributy (v nich uchováváme hodnoty) a metody (vykonávají nějaké příkazy). Atributy jsou vlastně proměnné, pouze jsou navázané na konkrétní objekt. Funkce jsme poznali v předchozí kapitole, metody jsou pak funkce navázané na konkrétní objekt a pracují s jeho atributy.

Popišme si konkrétně náš příklad software pro firmy. V něm můžeme mít například třídu Zamestnanec, který reprezentuje zaměstnance. Třída může mít jméno, pracovní pozici, oddělení, plat, zbývající dny dovolené atd. Zaměstnanec může mít i metody - například metodu na vybrání dovolené, vytištění výplatní pásky, výpočet věku atd.

Před vytvářením objektů je třeba mít připravenou třídu, na základě které objekt vznikne. K tomu použijeme klíčové slovo class. Za něj přijde název třídy a opět dvojtečka. Pro začátek si vytvořme třídu jen s jednou metodou vypis_informace, která vypíše informace o zaměstnanci.

Všimni si parametru self u metody. Pomocí self se odkazujeme na atributy objektu. Pokud chceme získat hodnotu atributu, napíšeme klíčové slovo self, tečku a název atributu. Tečky při práci s objekty používáme velmi často a jsou jakousi analogií k hranatým závorkám u sekvencí.

class Zamestnanec:
  def vypis_informace(self):
    return f"{self.jmeno} pracuje na pozici {self.pozice}."

Protože pracujeme s metodou, můžeme (ale nemusíme) použít klíčové slovo return a vrátit nějakou hodnotu.

Zkusme si nyní vytvořit objekt, který reprezentuje zaměstnance Františka. Objekt vytvoříme podobně, jako bychom volali funkci - použijeme název třídy a kulaté závorky. Objekt uložíme do proměnné frantisek. Dále přiřadíme proměnné frantisek hodnoty atributů jmeno a pozice a vyzkoušíme metodu vypis_informace.

frantisek = Zamestnanec()
frantisek.jmeno = "František Novák"
frantisek.pozice = "konstruktér"
print(frantisek.vypis_informace())

Zkusíme přidat ještě jednu zaměstnankyni.

klara = Zamestnanec()
klara.jmeno = "Klára Nová"
klara.pozice = "konstruktérka"

Nyní vyzkoušíme vypsat informace obou zaměstnanců.

print(frantisek.vypis_informace())
print(klara.vypis_informace())

Metoda

Z výpis vidíme, že se informace zaměstnanců nijak nepomíchaly a každý zaměstnanec má uložené své vlastní údaje.

Tento postup ale působí lehce chaoticky. V naší analogii s formuláři to vypadá, že si každý může do formuláře vyplnit, co chce. Abychom měli objekt více pod kontrolou, můžeme využít metodu __init__ (název zapisujeme včetně podtržítek). Tato metoda je speciální v tom, že je zavolána při vytvoření objektu. Můžeme jí (jako jakékoli jiné metodě) přiřadit parametry a zajistit, aby hodnoty parametrů uložila jako atributy objektu.

class Zamestnanec:
  def vypis_informace(self):
    return f"{self.jmeno} pracuje na pozici {self.pozice}."
  def __init__(self, jmeno, pozice):
    self.jmeno = jmeno
    self.pozice = pozice

Tento styl je standardní - parametry jsou pojmenované stejně jako atributy objektu, kam se jejich hodnoty ukládají. Mezi self.jmeno a jmeno je důležitý rozdíl:

  • jmeno je parametr metody __init__ a jeho hodnota není přístupná pro ostatní funkce objektu.
  • self.jmeno je atribut objektu, který v objektu zůstane a můžou s ním pracovat ostatní metody.

Díky metodě __init__ máme zjednodušené vytváření objektu, protože hodnoty parametrů nyní vepíšeme přímo do závorek při vytváření objektu.

frantisek = Zamestnanec("František Novák", "konstruktér")
klara = Zamestnanec("Klára Nová", "svářeč")

print(frantisek.vypis_informace())
print(klara.vypis_informace())

Nyní již víme, že každý objekt třídy Zamestnanec má vyplněné jméno a pozici. Zkusme nyní naši třídu obohatit o novou metodu - čerpání dovolené. Na začátku bude mít každý zaměstnanec nárok na dovolenou, kterou může v průběhu roku čerpat. Čerpání zajistíme pomocí metody cerpani_dovolene. Budeme hlídat i to, aby zaměstnanec nárok na dovolenou nepřečerpal.

class Zamestnanec:
  def cerpani_dovolene(self, days):
    if self.pocet_dni_dovolene >= days:
      self.pocet_dni_dovolene -= days
      return f"Užij si to."
    else:
      return f"Bohužel už máš nárok jen na {self.pocet_dni_dovolene} dní."
  
  def vypis_informace(self):
    return f"{self.jmeno} pracuje na pozici {self.pozice}."
    
  def __init__(self, jmeno, pozice):
    self.jmeno = jmeno
    self.pozice = pozice
    self.pocet_dni_dovolene = 25

Nyní se podívejme, jak budou vyřizovány Františkovy žádosti o dovolenou.

frantisek = Zamestnanec("František Novák", "konstruktér")

print(frantisek.cerpani_dovolene(5))
print(frantisek.cerpani_dovolene(15))
print(frantisek.cerpani_dovolene(10))

Metoda __str__

Pojďme ještě použití naší třídy trochu zjednodušit. Naše třída umí přehledně vypsat informace díky metodě cerpani_dovolene(). Třídu ale může používat i jiný programátor a ten o této metodě nemusí vědět a tak intuitivně vyzkouší funkci print(), které vloží nějaký objekt třídy Zamestnanec.

print(frantisek)

Odpovědí bude poněkud záhadný text ve stylu

<__main__.Zamestnanec object at 0x00000126F0084850>

Funke print() se totiž pokusí převést objekt na typ řetězec. Protože naše třída nemá tuto funkci naprogramovanou, použije se standardní formát, který nám říká, že jde o objekt třídy Zamestnanec a kde je uložený v paměti. Bylo by však dobré místo toho získat nějaký srozumitelný výpis, třeba takový, který poskytuje metoda vypis_informace().

Převod na řetězec zařídíme tím, že třídě přidáme metodu __str__. Dvě lomítka opět značný zvláštní význam. Ten spočívá v tom, že Python využije tuto metodu vždy, když jej požádáme o převod objektu na řetězec. Můžeme tedy přejmenovat metodu vypis_informace() na __str__. Výstupem našeho programu pak bude text o tom, jak se zaměstnanec jmenuje a kde pracuje.

class Zamestnanec:
  def __str__(self):
    return f"{self.jmeno} pracuje na pozici {self.pozice}."
  def __init__(self, jmeno, pozice):
    self.jmeno = jmeno
    self.pozice = pozice

frantisek = Zamestnanec("František Novák", "konstruktér")
print(str(frantisek))

Tím jsme si ukázali, jak vytvořit třídu, objekty a jak s nimi pracovat.

Cvičení: Objekty a třídy

1

Kniha

to dáš

Zkus pro našeho nakladatele vytvořit software s využitím tříd a objektů. Vytvoř tedy třídu Book, která reprezentuje knihu. Každá kniha bude mít atributy title, pages a price. Hodnoty nastav ve funkci __init__.

  • Přidej knize funkci getInfo, která vypíše informace o knize v nějakém pěkném formátu.
  • Občas se stane, že se kniha moc neprodává a knihkupec se snaží nalákat kupující slevou. Přidej funkci discount, která bude mít jeden parametr - velikost slevy v procentech. Funkce sníží cenu knihy o dané procento.
2

Balík

zapni hlavu

Uvažuj, že navrhuješ software pro zásilkovou společnost.

  • Vytvoř třídu Package, která bude mít tři atributy - address, weightInKilos a delivered. První dva atributy nastav pomocí parametrů funkce __init__. Parametr delivered nastav na začátku jako False.
  • Připoj ke třídě funkci deliver, která změní hodnotu parametru delivered na True.
  • Přidej funkci getInfo, která vypíše adresu, hmotnost a informaci o tom, zda byl balík již doručen.
  • Zkus si vytvořit nějaké objekty ze třídy Package a ověř, že vše funguje.

Bonusy

3

Zkušební doba

zavařovačka

U zaměstnanců budeme nově evidovat, jestli jsou ve zkušební době.

  • Rozšiř funkci __init__ třídy Employee o parametr probation, který bude typu bool. Tuto hodnotu ulož jako atribut třídy Employee.
  • Uprav funkci getInfo. Pokud je zaměstnanec ve zkušební době, přidej k jeho/jejímu výpisu text Je ve zkušební době.