Janko a Marienka chcú spojiť doterajšie vedomosti do študentského systému. Pomocou objektov v ňom prepoja údaje o študentoch s operáciami, ktoré s nimi pracujú.
Čo sa naučíte
Po skončení kapitoly budete vedieť:
- vytvoriť triedu a inicializovať jej inštancie;
- pridávať atribúty a metódy;
- používať dedičnosť a prepisovať metódy;
- prispôsobiť správanie objektu pomocou špeciálnych metód.
Janko začal študovať v Košiciach a dostal zadanie vytvoriť systém, ktorý bude evidovať študentov, ich predmety a výsledky. S Marienkou najskôr chceli uložiť všetky údaje do samostatných premenných, no pri desiatkach študentov by v nich rýchlo vznikol chaos.
Rozhodli sa preto použiť objektovo orientované programovanie. Údaje a funkcie, ktoré s nimi pracujú, spoja do jedného celku — objektu.
Trieda a objekt
Trieda je plán, podľa ktorého Python vytvára objekty. Janko a Marienka začnú
prázdnou triedou Student:
class Student:
pass
Z tejto triedy už vytvoria konkrétny objekt:
janko = Student()
Student je trieda, zatiaľ čo janko je jej inštancia — samostatný objekt uložený v pamäti.
Prázdny študent však systému veľmi nepomôže. Každý potrebuje meno, vek a študijný program. Tieto údaje nastaví metóda __init__, ktorá sa spustí automaticky pri vytvorení objektu.
class Student:
def __init__(self, name, age, program):
self.name = name
self.age = age
self.program = program
Parameter self označuje objekt, s ktorým metóda práve pracuje. Výrazy self.name, self.age a self.program vytvárajú jeho atribúty.
Teraz môžu rovnakým spôsobom vytvoriť viacerých študentov:
janko = Student("Janko", 20, "Počítačové siete")
marienka = Student("Marienka", 21, "Aplikovaná matematika")
Oba objekty vznikli podľa rovnakej triedy, no každý má vlastné údaje. K atribútom sa pristupuje bodkovou notáciou:
print(janko.name)
print(marienka.program)
janko.age = 21
Metódy: čo objekt dokáže
Pri testovaní si Marienka uvedomila, že systém údaje iba uchováva. Študent by sa mal vedieť aj predstaviť a zapísať si predmet. Do triedy preto pridajú metódy:
class Student:
def __init__(self, name, age, program):
self.name = name
self.age = age
self.program = program
self.courses = []
def introduce(self):
print(f"Som {self.name} a študujem program {self.program}.")
def enroll(self, course):
if course not in self.courses:
self.courses.append(course)
Metóda patrí triede, ale pomocou self pracuje s údajmi konkrétneho objektu:
janko.enroll("Programovanie")
janko.enroll("Počítačové siete")
janko.introduce()
Takto objekt spravuje vlastný stav. Kód, ktorý používa triedu, nemusí riešiť, ako sú údaje uložené.
Atribút spoločný pre všetkých
Meno, vek aj predmety sa medzi študentmi líšia. Univerzita je však pre všetkých rovnaká, preto ju uložia ako atribút triedy:
class Student:
university = "Technická univerzita v Košiciach"
def __init__(self, name, age, program):
self.name = name
self.age = age
self.program = program
self.courses = []
K spoločnej hodnote môže pristúpiť cez triedu aj cez objekt:
print(Student.university)
print(janko.university)
Atribúty vytvorené pomocou self patria jednotlivým inštanciám. Atribút university patrí triede a zdieľajú ho všetky jej inštancie.
Dedičnosť: z obyčajného študenta asistent
Projekt zaujal vyučujúceho natoľko, že Jankovi ponúkol miesto pomocného asistenta v laboratóriu. Asistent je stále študent, má však navyše pridelené laboratórium. Namiesto kopírovania celej triedy použijú dedičnosť:
class TeachingAssistant(Student):
def __init__(self, name, age, program, laboratory):
super().__init__(name, age, program)
self.laboratory = laboratory
TeachingAssistant je podtrieda triedy Student. Volanie super().__init__() použije konštruktor rodičovskej triedy a potom podtrieda pridá vlastný atribút.
assistant = TeachingAssistant(
"Janko",
21,
"Počítačové siete",
"Laboratórium sieťových technológií",
)
Objekt assistant má všetky atribúty a metódy študenta, navyše však pozná svoje laboratórium.
Prepísanie metódy
Pôvodné predstavenie už asistentovi nestačilo. V podtriede preto Janko vytvoril metódu s rovnakým názvom:
class TeachingAssistant(Student):
def __init__(self, name, age, program, laboratory):
super().__init__(name, age, program)
self.laboratory = laboratory
def introduce(self):
super().introduce()
print(f"Zároveň pomáham v laboratóriu {self.laboratory}.")
Podtrieda tým prepísala zdedenú metódu. Najprv cez super() použije pôvodné predstavenie a potom ho rozšíri o novú informáciu.
Špeciálne metódy
Marienka pri testovaní skúsi vypísať celý objekt:
print(janko)
Výsledok nebol veľmi užitočný. Python však pozná špeciálnu metódu __str__, ktorá určuje, ako sa má objekt zobraziť používateľovi:
class Student:
university = "Technická univerzita v Košiciach"
def __init__(self, name, age, program):
self.name = name
self.age = age
self.program = program
self.courses = []
def __str__(self):
return f"{self.name} — {self.program}"
Po tejto zmene príkaz print(janko) vypíše:
Janko — Počítačové siete
Na ladenie sa používa aj metóda __repr__. Jej výsledok má byť presný a užitočný najmä pre programátora:
def __repr__(self):
return (
f"Student(name={self.name!r}, age={self.age!r}, "
f"program={self.program!r})"
)
Iterovanie cez predmety
Janko chce prechádzať zapísané predmety priamo pomocou cyklu. Do triedy preto
pridá metódu __iter__:
def __iter__(self):
return iter(self.courses)
Objekt sa teraz správa ako kolekcia predmetov:
for course in janko:
print(course)
Výsledky ako v slovníku
Napokon potrebujú evidovať aj body zo skúšok. Marienka pridá slovník scores
a dve špeciálne metódy:
class Student:
def __init__(self, name, age, program):
self.name = name
self.age = age
self.program = program
self.courses = []
self.scores = {}
def __getitem__(self, subject):
return self.scores[subject]
def __setitem__(self, subject, score):
self.scores[subject] = score
Vďaka __getitem__ a __setitem__ môže s výsledkami pracovať podobne ako so slovníkom:
janko["Programovanie"] = 95
print(janko["Programovanie"])
Návratová hodnota namiesto print()
Metóda určená na výpočet alebo získanie údaja by mala často hodnotu vrátiť. Volajúci ju potom môže vypísať, uložiť alebo ďalej spracovať.
class Student:
def __init__(self, name):
self.name = name
self.courses = []
def course_count(self):
return len(self.courses)
janko = Student("Janko")
janko.courses.append("Python")
print(janko.course_count()) # 1
Častá chyba: meniteľný atribút triedy
Zoznamy a slovníky, ktoré patria konkrétnej inštancii, vytvárame v
__init__. Ak by sme napísali courses = [] priamo do tela triedy, všetci
študenti by zdieľali jeden spoločný zoznam predmetov.
Cvičenia
- Vytvorte triedu
Student, ktorá bude obsahovať meno, zoznam predmetov a slovník známok. Pridajte metódy na zápis predmetu, pridanie známky, výpočet priemeru a čitateľné zobrazenie študenta. Vytvorte aspoň dve inštancie a overte, že ich predmety a známky sa navzájom nemiešajú.
Zhrnutie
Janko a Marienka dokončili študentský systém. Trieda opisuje spoločnú štruktúru a správanie, inštancia obsahuje konkrétne údaje. Atribúty inštancie patria objektu, atribúty triedy sú spoločné a dedičnosť umožňuje rozšíriť existujúcu triedu.