2. diel - Prvá objektová aplikácia v Pythone - Hello object world
Minulá lekcia, Úvod do objektovo orientovaného programovania v Pythone, nás uviedla do objektovo orientovaného programovania.
Už vieme, že objekty majú atribúty a metódy. Tiež vieme, že na vytvorenie objektu vytvoríme najprv triedu. Tá je vzorom, podľa ktorého následne tvoríme jej inštancie.
Na začiatku kurzu so základnými štruktúrami jazyka Python sme si vytvorili program Hello world. V tomto tutoriále Objektovo orientovaného programovania v Pythone si napíšeme podobný program, avšak tentoraz v paradigme OOP. Naprogramujme si Hello object world !
Vytvorenie triedy
V IDE založíme nový projekt s názvom HelloObjects
a
vytvoríme súbor greeter.py
. Triedy sa vytvárajú kľúčovým
slovom class
. Triedu pomenujeme Greeter
. V triede bude
blok kódu, takže za meno triedy píšeme dvojbodku. Prvý riadok teda vyzerá
takto:
class Greeter:
Názov súboru (modulu) píšeme notáciou snake_case
a triedy
naopak notáciou PascalCase
. Na rozdiel od premenných má teda
každé slovo v názve triedy veľké prvé písmeno. Názov je samozrejme tiež
bez diakritiky, ktorú v programe používame maximálne vo vnútri textových
reťazcov, nie v identifikátoroch. Podľa tejto triedy neskôr vytvoríme
objekt greeter
, ktorý nás bude vedieť pozdraviť. Vidíme, že
sa na program už pozeráme úplne inak, za každú akciu je zodpovedný nejaký
objekt. V našom prípade sa to môže zdať zbytočné, avšak pri
zložitejších aplikáciách si to budete pochvaľovať
Teraz si do triedy Greeter
pridáme metódu
greet()
. Metóda bude verejne viditeľná a nebude mať žiadnu
návratovú hodnotu ani atribúty.
Deklarácia metódy v Pythone je podobná deklarácii funkcie. Za
kľúčovým slovom def
nasleduje samotný názov metódy. Metódy
píšeme rovnako ako premenné a funkcie malými písmenami. V prípade
viacslovného názvu použijeme notáciu snake_case
.
Zátvorka s parametrami je povinná. Prvý povinný pozičný
argument je self
. Do neho sa vloží "odkaz" na objekt, do ktorého
metóda patrí. Tento argument tam vloží sám objekt. Do tela
metódy zapíšeme kód na výpis do konzoly.
Naša trieda bude teraz vyzerať takto:
class Greeter: def greet(self): print("Hello object world!")
Všimnime si, že Pythonu dávame najavo odsadením, že metóda
greet()
je súčasťou triedy Greeter
.
Teraz si vytvoríme inštanciu triedy Greeter
.
Bude to teda ten objekt greeter
, s ktorým budeme pracovať.
Objekty sa ukladajú do premenných, názov triedy slúži ako dátový typ.
Inštancia má spravidla názov triedy, len má prvé písmeno malé. Deklarujme
si teda premennú a následne v nej založme novú inštanciu triedy
Greeter
:
greeter = Greeter()
Pri vytvorení novej inštancie sa zavolá tzv. konštruktor. To je špeciálna metóda na triede, preto pri vytvorení inštancie píšeme tie prázdne zátvorky, pretože voláme túto "vytváraciu" metódu. Konštruktor spravidla obsahuje nejakú inicializáciu vnútorného stavu inštancie (napr. dosadí východiskové hodnoty do premenných). My sme v kóde žiadny konštruktor nedeklarovali a Python si preto vytvoril tzv. implicitný prázdny konštruktor. Vytvorenie inštancie objektu je teda podobné volanie metódy.
Keďže v premennej teraz máme naozaj inštanciu triedy
Greeter
, môžeme inštanciu nechať pozdraviť. Zavoláme na ňu
metódu greet()
a to ako greeter.greet()
. Náš kód
bude teda teraz vyzerať nasledovne:
class Greeter:
def greet(self):
print("Hello object world!")
greeter = Greeter()
greeter.greet()
Máme teda svoju prvú objektovú aplikáciu!
Pridanie parametra
Dajme teraz našej metóde greet()
parameter name
,
aby dokázala pozdraviť konkrétneho užívateľa:
def greet(self, name): print(f"Hello {name}!")
Vidíme, že syntax parametra metódy je rovnaká, ako syntax premennej. Jednotlivé parametre oddeľujeme čiarkou. Upravíme tiež kód pod triedou:
class Greeter:
def greet(self, name):
print(f"Hello {name}!")
greeter = Greeter()
greeter.greet("Carl")
greeter.greet("Peter")
Náš kód je teraz v metóde a my ho môžeme jednoducho pomocou parametrov volať znova s rôznymi parametrami. Nemusíme dvakrát opisovať "Hello...". Odteraz budeme deliť kód logicky do metód:
Output of the greet() method:
Hello Carl!
Hello Peter!
Pridanie atribútu
Triede pridáme nejaký atribút. Ponúka sa text
, kde bude
uložený text pozdravu. Atribúty sa definujú rovnako ako premenné. Pred ich
názov píšeme self
. Upravme teda našu triedu:
class Greeter:
text = "undefined" # defining the attribute
def greet(self, name):
print(f"{self.text} {name}!") # here we use self before the attribute name
greeter = Greeter()
greeter.text = "Hello"
greeter.greet("Carl")
greeter.greet("Peter")
greeter.text = "Hello programmer"
greeter.greet("Richard")
Program teraz zobrazí:
Output of the greet() method:
Hello Carl!
Hello Peter!
Hello programmer Richard!
Vrátenie hodnoty
Vzhľadom k objektovému návrhu nie je najvhodnejšie, aby si každý objekt ovplyvňoval vstup a výstup ako sa mu zachce. Pochopiteľne mierime na naše vypisovanie do konzoly. Každý objekt by mal mať určité kompetencie, ktoré by nemal prekračovať. Poverme náš objekt iba zostavením pozdravu a jeho výpis si spracujeme už mimo. Výhodou takto navrhnutého objektu je vysoká univerzálnosť a znovupoužiteľnosť. Objekt doteraz vie len písať do konzoly. My si ho však prispôsobíme tak, aby daná metóda text iba vracala. Bude potom iba na jeho príjemcovi, ako s ním naložia. Takto môžeme pozdravy ukladať do súborov, písať na webové stránky alebo ďalej spracovávať.
Na návrat hodnoty použijeme príkaz return
. Return metódu
ukončí a vráti jej hodnotu. Akýkoľvek ďalší kód v tele metódy sa po
return
už nevykoná!
Upravíme metódu greet()
:
def greet(self, name): return f"{self.text} {name}!"
A náš program bude teraz vyzerať takto:
class Greeter: text = "Hello" def greet(self, name): return f"{self.text} {name}!" greeter = Greeter() greeter.text = "Hello user" print(greeter.greet("Carl")) print(greeter.greet("Peter")) greeter.text = "Hello programmer" print(greeter.greet("Richard"))
Kód je teraz napísaný podľa dobrých praktík.
Pridanie komentárov
Ešte našu triedu ako sa pristane a patrí okomentujme. Komentáre budeme
písať pod názov triedy a pod názov každého atribútu a metódy. Na ich
zápis použijeme tri dvojité úvodzovky ("""
). Správne
zdokumentovaná trieda vyzerá napr. takto:
class Greeter: """ A class represents a greeter whose purpose is to greet the user. """ text = "Hello" """ An attribute containing the default greeting text.If no other text is specified, this default text will be used when creating a greeting. """ def greet(self, name): """ Returns a greeting for the user with the set text and given name. Parameters: - name(str): The given name of the person we want to greet. Output: - str: A greeting text with the person's given name. """ return f"{self.text} {name}!"
Vstavaná nápoveda - funkcia
help()
Pri čítaní kódu často narazíme na triedu, inštanciu, premennú atď.,
ktorej význam a účel nie je na prvý pohľad jasný. Funkcia
help()
nám pomôže - zobrazí o danom objekte všetky dostupné
detaily. Zoberme našu starostlivo zdokumentovanú triedu Greeter
a
vložme ju do nového súboru, napríklad greeter_hlp.py
. Súbor si
uložíme a spustíme (spustenie programu je nutné, inak funkcia
help()
vyhodí chybu). V IDE sa potom prepnime do interaktívnej
konzoly. Pre PyCharm je v úplne vľavo ako prvá z dolného stĺpca ikon
označená ako Python Console:
Do interaktívnej konzoly zadáme príkazy:
from greeter_hlp import Greeter # We first need to import the class into the console help(Greeter)
Tým sa nám zobrazí jej popis aj popis všetkých jej metód a atribútov:
Output of the help() function:
class Greeter(builtins.object)
| A class represents a greeter whose purpose is to greet the user.
|
| Methods defined here:
|
| greet(self, name)
| Returns a greeting for the user with the set text and given name.
|
| Parameters:
| - name (str): The given name of the person we want to greet.
|
| Output:
| - str: A greeting text with the person's given name.
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| text = 'Hello'
Pokiaľ chceme v PyCharm informácie iba o konkrétnej metóde či premennej, nastavíme na ňu kurzor myši a stlačíme Ctrl + Q.
Význam self
V Pythone je self
jedným z kľúčových konceptov v objektovo
orientovanom programovaní a je dôležité ho pochopiť. Keď už vieme, čo je
inštancia a ako sa tvoria atribúty, poďme si ho vysvetliť jednoducho a
názorne.
Keď vytvárame objekt (inštanciu triedy), predstavíme si ho ako
špeciálny box. Každý box (objekt) má svoje vlastné dáta (atribúty) a
nástroje, ktoré s týmto boxom je možné robiť (metódy). Keď chceme, aby
box (objekt) niečo urobil, potrebujeme spôsob, ako na tento konkrétny box
odkazovať. A práve self
je spôsob, ktorým to dosiahneme.
Technické detaily self
V Pythone sa metóda vždy automaticky volá s referenciou na objekt,
s ktorým bola zavolaná. Tento objekt sa odovzdáva ako prvý
parameter metódy. Konvenciou je, že sa tento parameter nazýva
self
. Keď voláme metódu objektu, napr.
my_object.my_method()
, nemusíme self
odovzdávať
explicitne. Python to za nás urobí automaticky.
Názov prvého parametra – self
– môže byť
aj iný, ale my sa budeme držať zavedených praktík.
Prečo self
potrebujeme
Keď máme viac boxov (objektov) rovnakého typu (triedy), musí metóda
vedieť, s ktorým konkrétnym boxom má pracovať. Predstavme si, že máme v
kancelárii a doma dva identické kávovary, pretože kávu nie je nikdy dosť
Oba kávovary sú rovnakého
modelu (triedy). Každý kávovar má teda metódu add_water()
,
ktorá slúži na doplnenie vody do daného kávovaru. V tomto kontexte je
self
kľúčový.
Ako identifikujeme, ktorému kávovaru došla voda? Keby kávovary mohli
hovoriť, povedal by nám kávovar doma, ktorému skutočne došla voda: „To
ja som na suchu!“. V Pythone pre to máme self
. Vďaka nemu mu
dokážeme nariadiť: "OK, kávovar (self)
, doplň si vodu."
Ukážme si príklad reálneho kódu:
class CoffeeMaker:
location = "unknown location"
water_amount = 0
def add_water(self, amount):
self.water_amount += amount
print(f"{amount} ml of water has been added to the coffee maker at {self.location}."
f" It now contains {self.water_amount} ml of water.")
def brew_coffee(self):
if self.water_amount > 100:
self.water_amount -= 100
print(f"The coffee maker at {self.location} brewed coffee. {self.water_amount} ml of water remains.")
else:
print(f"There is not enough water in the coffee maker at {self.location}!")
# Create two instances of the coffee maker
coffee_maker_at_home = CoffeeMaker()
coffee_maker_at_office = CoffeeMaker()
# Set the location for both coffee makers
coffee_maker_at_home.location = "home"
coffee_maker_at_office.location = "the office"
# Add water to the coffee maker at the office
coffee_maker_at_office.add_water(150)
# Try to brew coffee in the coffee maker at home
coffee_maker_at_home.brew_coffee()
V konzole uvidíme výstup:
Usage of self:
150 ml of water has been added to the coffee maker at the office. It now contains 150 ml of water.
There is not enough water in the coffee maker at home!
Bez použitia self
v self.location
a
self.water_amount
by metódy add_water()
a
brew_coffee()
nemali šancu zistiť, či ich využíva inštancia
kávovaru doma alebo toho v kancelárii. self
tu teda slúži ako
odkaz na konkrétnu inštanciu kávovaru. Pokiaľ by sme self
v
metódach triedy nepoužili, tieto metódy by nemali prístup k dátam
uloženým v konkrétnej inštancii triedy a nemohli by vykonávať
operácie špecifické pre tú danú inštanciu.
A sme na konci. Za úlohu máte prerobiť si našu konzolovú kalkulačku do objektov.
V nasledujúcom kvíze, Kvíz - Úvod, objekty, triedy a metódy v Pythone, si vyskúšame nadobudnuté skúsenosti z predchádzajúcich lekcií.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 0x (1.82 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Python