Letní akce! Lákají tě IT školení C#, Javy a PHP v Brně? Přihlas se a napiš nám do zpráv kód "BRNO 500" pro slevu 500 Kč na libovolný brněnský kurz. Lze kombinovat se slevami uvedenými u školení i použít pro více kurzů. Akce končí 28.7.

Lekce 2 - První objektová aplikace v Pythonu - Hello object world

Python Objektově orientované programování První objektová aplikace v Pythonu - Hello object world

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

Minulá lekce, Úvod do objektově orientovaného programování v Pythonu, nás uvedla do objektově orientovaného programování. Již víme, že objekty mají atributy a metody. Také víme, že k vytvoření objektu vytvoříme nejprve třídu. Ta je vzorem, podle kterého následně tvoříme její instance.

Na začátku kurzu se základními strukturami jazyka Python jsme si vytvořili program Hello world. Udělejme si v dnešním tutoriálu podobný program jako úvod do objektově orientovaného programování. Naprogramujme si Hello object world!

Otevřeme IDLE (nebo jiný editor) a otevřeme nový soubor. Třídy se vytvářejí klíčovým slovem class. Třídu pojmenujeme Zdravic. Ve třídě bude blok kódu, takže za jméno třídy píšeme dvojtečku. První řádka tedy vypadá takto:

class Zdravic:

Název třídy píšeme vždy velbloudí notací bez mezer a na rozdíl od proměnných má každé slovo v názvu velké první písmeno. Název je samozřejmě také bez diakritiky, tu v programu používáme maximálně uvnitř textových řetězců, nikoli v identifikátorech. Podle této třídy později vytvoříme objekt zdravič, který nás bude umět pozdravit. Vidíme, že se na program již díváme úplně jinak, za každou akci je zodpovědný nějaký objekt. V našem případě vám to může přijít zbytečné, u složitějších aplikací si to budete pochvalovat :)

Nyní si do třídy Zdravic přidáme metodu pozdrav(), bude veřejně viditelná a nebude mít žádnou návratovou hodnotu ani atributy.

Deklarace metody v Pythonu je podobná deklaraci funkce. Za klíčovým slovem def následuje samotný název metody, metody píšeme stejně jako proměnné a funkce malými písmeny. V případě víceslovného názvu použijeme podtržítka. Závorka s parametry je povinná. První povinný poziční argument je self. Do něj se vloží "odkaz" na objekt, do kterého metoda náleží. Tento argument tam vloží sám objekt. Do těla metody zapíšeme kód pro výpis do konzole.

Naše třída bude nyní vypadat takto:

class Zdravic:

    def pozdrav(self):
        print("Hello object world!")

Název prvního parametru - self - může být i jiný, ale my se budeme držet zavedených praktik.

Nyní si vytvoříme instanci třídy Zdravic. Bude to tedy ten objekt zdravič, se kterým budeme pracovat. Objekty se ukládají do proměnných, název třídy slouží jako datový typ. Instance má zpravidla název třídy, jen má první písmeno malé. Deklarujme si proměnnou a následně v ní založme novou instanci třídy Zdravic:

zdravic = Zdravic()

Při vytvoření nové instance se zavolá tzv. konstruktor. To je speciální metoda na třídě, proto při vytvoření instance píšeme ty prázdné závorky, jelikož voláme tuto "vytvářecí" metodu. Konstruktor zpravidla obsahuje nějakou inicializaci vnitřního stavu instance (např. dosadí výchozí hodnoty do proměnných). My jsme v kódu žádný konstruktor nedeklarovali, Python si proto vytvořil tzv. implicitní prázdný konstruktor. Vytvoření instance objektu je tedy podobné volání metody.

Jelikož v proměnné nyní máme opravdu instanci třídy Zdravic, můžeme instanci nechat pozdravit. Zavoláme na ni metodu pozdrav a to jako zdravic.pozdrav(). K aplikaci přidáme ještě input(). Náš kód bude tedy nyní vypadat následovně:

class Zdravic:

    def pozdrav(self):
        print("Hello object world!")

zdravic = Zdravic()
zdravic.pozdrav()
input()

Program spustíme.

První objektová aplikace v Python – Hello object world

Máme tedy svou první objektovou aplikaci!

Dejme nyní naší metodě pozdrav() parametr jmeno, aby dokázala pozdravit konkrétního uživatele:

def pozdrav(self, jmeno):
    print("Ahoj uživateli {0}!".format(jmeno))

Vidíme, že syntaxe parametru metody je stejná, jako syntaxe proměnné. Jednotlivé parametry oddělujeme čárkou. Upravíme také kód pod třídou:

zdravic = Zdravic()
zdravic.pozdrav("Karel")
zdravic.pozdrav("Petr")
input()

Náš kód je nyní v metodě a my ho můžeme jednoduše pomocí parametrů volat znovu s různými parametry. Nemusíme 2x opisovat "Ahoj uživateli...". Odteď budeme dělit kód logicky do metod.

Pozdravení uživatelů v objektové aplikaci v Pythonu

Třídě přidáme nějaký atribut, nabízí se text, kde bude uložen text pozdravu. Atributy se definují stejně, jako proměnné. Před jejich název píšeme self. Upravme naši třídu:

def pozdrav(self, jmeno):
    print("{0} {1}!".format(self.text, jmeno))

Avšak díky tomu, že zatím nemáme metodu konstruktoru, tak musíme atribut text inicializovat před zavoláním metody, která tento atribut vyžaduje.

zdravic = Zdravic()
zdravic.text = "Ahoj uživateli"
zdravic.pozdrav("Karel")
zdravic.pozdrav("Petr")
zdravic.text = "Vítám tě tu programátore"
zdravic.pozdrav("Richard")
input()

Program nyní zobrazí:

Pozdravení uživatelů v objektové aplikaci v Pythonu

Vzhledem k objektovému návrhu není nejvhodnější, aby si každý objekt ovlivňoval vstup a výstup, jak se mu zachce. Pochopitelně narážím na naše vypisování do konzole. Každý objekt by měl mít určitou kompetenci a tu by neměl překračovat. Pověřme náš objekt pouze sestavením pozdravu a jeho výpis si zpracujeme již mimo. Výhodou takto navrženého objektu je vysoká univerzálnost a znovupoužitelnost. Objekt doposud umí jen psát do konzole, my ho však přizpůsobíme tak, aby daná metoda text pouze vracela a bylo na jeho příjemci, jak s ním naloží. Takto můžeme pozdravy ukládat do souborů, psát na webové stránky nebo dále zpracovávat.

K návratu hodnoty použijeme příkaz return. Return metodu ukončí a navrátí hodnotu. Jakýkoli další kód v těle metody po return se již neprovede! Upravme třídu:

Metoda pozdrav:

def pozdrav(self, jmeno):
    return "{0} {1}!".format(self.text, jmeno)

Hlavní část programu:

zdravic = Zdravic()
zdravic.text = "Ahoj uživateli"
print(zdravic.pozdrav("Karel"))
print(zdravic.pozdrav("Petr"))
zdravic.text = "Vítám tě tu programátore"
print(zdravic.pozdrav("Richard"))
input()

Nyní je náš kód dle dobrých praktik. Ještě naši třídu okomentujme, jak se sluší a patří. Komentáře budeme psát pod název třídy a pod název každého atributu a metody. K jejich zápisu použijeme tři dvojité uvozovky ("""). Jestliže do interaktivní konzole v IDLE (příkazová řádka Windows nepodporuje unicode) zadáme příkaz help() a jako parametr napíšeme název třídy, zobrazí její popis i popis všech jejích metod. Zdokumentovaná třída může vypadat např. takto:

class Zdravic:
    """
    Třída reprezentuje zdravič, který slouží ke zdravení uživatelů.
    """

    def pozdrav(self, jmeno):
        """
        Vrátí pozdrav uživatele s nastaveným textem a jeho jménem.
        """
        return "{0} {1}!".format(self.text, jmeno)

Podíváme se, že se nám popisky zobrazí po importování modulu a napsání příkazu help():

Metody objektu zdravic v Python Shellu

A jsme u konce. Námi napsaný program má již nějakou úroveň, i když vlastně nic nedělá. Za úkol máte předělat si naši konzolovou kalkulačku do objektů. V příští lekci, Hrací kostka v Pythonu - Konstruktory a náhodná čísla, si uděláme takovou jednoduchou hříčku. Necháme dva objekty (bojovníky) soupeřit v aréně (také objektu). Máte se na co těšit ;-)


 

Stáhnout

Staženo 202x (545 B)
Aplikace je včetně zdrojových kódů v jazyce python

 

 

Článek pro vás napsal gcx11
Avatar
Jak se ti líbí článek?
10 hlasů
(^_^)
Aktivity (2)

 

 

Komentáře
Zobrazit starší komentáře (7)

Avatar
Lupti
Člen
Avatar
Lupti:13.2.2015 11:12

Diky moc

 
Odpovědět 13.2.2015 11:12
Avatar
Jan Říha
Člen
Avatar
Jan Říha:7.5.2016 19:14

Ahoj, toto je správně?
print("Ahoj uživateli {0}!".format(jme­no))
Honza

 
Odpovědět 7.5.2016 19:14
Avatar
Jan Říha
Člen
Avatar
Jan Říha:7.5.2016 19:31

Ahoj Honzo,
je to správně. Nebuď netrpělivý a pozorně čti návody.
Honza

 
Odpovědět  +2 7.5.2016 19:31
Avatar
Honza Bittner
Šupák
Avatar
Honza Bittner:24.2.2017 13:12

Btw. od verze 3.6 funguje i interpolace přímo ve stringu, který má prefix f, tj. například

f"Hi {name}, how are you?"
Odpovědět  +1 24.2.2017 13:12
Milovník Dartu. Student FIT ČVUT. Sleduj mě na https://twitter.com/tenhobi a ptej se na cokoli na https://github.com/...
Avatar
brevnovak
Člen
Avatar
brevnovak:22. února 12:57

me prijde hroze divny, ze instanci objektu muzeme libovone pridavat atributy:

zdravic.zvire="te­le"
print (zdravic.zvire)

cekal bych, ze to vyhodi chybu s tim, ze objekt zdravic zadny atribut zvire nemá..

 
Odpovědět 22. února 12:57
Avatar
gcx11
Redaktor
Avatar
Odpovídá na brevnovak
gcx11:22. února 19:46

To je z toho důvodu, jak jsou implementované objekty v Pythonu. Svoje atributy mají uložené ve slovníku __dict__, takže při přiřazení se do slovníku uloží nová položka s názvem atributu a jeho hodnotou.

 
Odpovědět 22. února 19:46
Avatar
Matěj K
Člen
Avatar
Matěj K:1. dubna 14:47

Ahoj,

vytvoril jsem si kalkulacku v OOP.
Mohl bych poprosit o radu, co by se dalo udelat lepe nebo jak udelat jinak, aby to bylo spravne pro objetkove programovani?
Snad bude odkaz pro cteni.

https://www.itnetwork.cz/…lighter/1053

Moc dekuju :)

 
Odpovědět 1. dubna 14:47
Avatar
Martin Petrovaj
Překladatel
Avatar
Odpovídá na Matěj K
Martin Petrovaj:1. dubna 16:19

Ahoj, kalkulačka vyzerá super a je aj okomentovaná, to sa u študentov často nevidí :-) Možno by som ti sem len vypísal pár možností na doladenie, ale inak je to celkom fajn:

  • v metóde podil používaš try/catch blok, aby si zachytil delenie nulou. Keďže v try máš len jeden príkaz tak to nie je nič strašné, ale do budúcna je dobré vedieť, že kód v try bloku sa vykonáva o čosi pomalšie než normálne. V prípade, že presne vieš, na čom môže daná časť kódu spadnúť (v tvojom prípade vtedy, ak y == 0), je lepšie túto situáciu ošetriť jednoduchou podmienkou na začiatku metódy, než odchytávať predvídateľnú výnimku. Tiež pozor na to, že pri súčasnej implementácii tvoja metóda volba napr. pri zadaní 0 / 1 nevypíše výsledok, ale bude výsledok 0 brať ako false positive.
  • V metóde voľba to už je síce pochopiteľnejšie a použitie try / catch dáva väčší zmysel, stále ale vieš pred konverziou pomerne jednoducho skontrolovať, či vstupný reťazec obsahuje len povolené znaky (číslice, des. bodku…) :-) Tiež často pri volaní volba opakuješ tie isté / veľmi podobné reťazce v parametroch, hlavne ten druhý - nedali by sa uložiť ako atribút kalkulačky pri volaní jej konštruktora?
  • k metóde cyklus: aby si sa nabudúce nemusel zdržiavať ošetrovaním veľkých / malých písmen, môžeš nabudúce vstup "uhladiť" metódou string.upper() alebo string.lower(). Zápis podmienok na konci metódy je síce formálne ok a v poriadku, len ťa chcem upozorniť, že zápis typu if podmienka: return True else: return False sú väčšinou programátorov vnímané ako trochu úsmevné. Chápem, že kvôli tomu, že tam je aj elif, aj else to z fleku nejde, ale je k dispozícii dosť alternatív:
# alternatíva 1:
nezadano = True
while nezadano:
        pokracovani = input("Chces pokracovat?[y/n]: ").lower()
        if len(pokracovani) == 1 and pokracovani in "yn":
                return pokracovani == "y"
        else:
                print("Spatne zadani")

# alternatíva 2:
pokracovani = ""
while pokracovani not in ("y", "n"):
        pokracovani = input("Chces pokracovat?[y/n]: ").lower()
else:
        return pokracovani == "y"

# tvoja verzia - v poriadku, ale pôsobí trochu redundantne
nezadano = True
while nezadano:
        pokracovani = input("Chces pokracovat?[y/n]:")
        if pokracovani == "y" or pokracovani == "Y":
                return True
        elif pokracovani == "n" or pokracovani == "N":
                return False
        else:
                print("Spatne zadani")

Čo sa týka princípov dodržiavania OOP, celkovo si nie som 100% istý tým, či by Kalkulačka mala robiť aj počítanie, aj výpis do konzoly a ešte si aj sama riadiť interakciu s používateľom v metóde cyklus… Medzi dobré praktiky patrí aj single responsibility principle, podľa ktorého by kalkulačka mala len pri volaní svojich metód počítať, všetky vstupy by mala dostávať odinakiaľ a tiež tam naspäť posielať výstupy, namiesto "svojvoľného" vypisovania do konzoly, k tomu by sa mala uchyľovať buď len pri vypisovaní podstatných chýb, alebo by ich ideálne mala len vyhodiť vyššie.

Na záver ti ako motiváciu na ďalšie učenie skúsim prihodiť ešte príklad trochu modulárnejšej kalkulačky :-)

class InvalidSquareRootError (Exception): pass

class Kalkulacka:

        def __init__(self):
                # Ukážka implementácie switchu v Pythone pomocou typu slovník (dict) s anonymnými metódami
                self.__operacie = {
                        '+': lambda x, y: x+y,
                        '-': lambda x, y: x-y,
                        '*': lambda x, y: x*y,

                        # Špeciálny zápis podmienky v anonymnej funkcii:
                        '/': lambda x, y: x/y if x!=0 else raise ZeroDivisionException("Chyba: Nemôžem deliť nulou"),
                        '**': lambda x, y: x**y,
                        #'odmoc': lambda x, y: raise InvalidSquareRootError("Chyba: Párna odmocnina záporného čísla") if y%2 and x<0 else x**(1/y)
                }

                self.__operands = []

        def __getitem__(self, strindex): return self.__operacie[strindex]
        def __setitem__(self, strindex, value): self.__operacie[strindex] = value


kalk = Kalkulacka()
# Kalkulačke vieme jednoducho dodefinovať nové operácie:
kalk["odmoc"] = lambda x, y: raise InvalidSquareRootError("Chyba: Párna odmocnina záporného čísla") if y%2 and x<0 else x**(1/y)

# S touto kalkulačkou viem vykonať ľubovoľnú v slovníku zadefinovanú operáciu:
operacia = kalk[input("Zadaj operáciu: ")]
vysledok = operacia(*kalk.operands)             # alebo kompaktnejšie: vysledok = kalk[input("Zadaj operáciu: ")](*kalk.operands)
# samozrejme si dávame pozor na naše vlastné výnimky + KeyError a ak si kalkulačka sama parsuje podsunutý vstup, tak aj na ValueError

Snáď som na nič nezabudol. Have fun coding!

Editováno 1. dubna 16:20
Odpovědět 1. dubna 16:19
if (this.motto == "") { throw new NotImplementedException(); }
Avatar
Matěj K
Člen
Avatar
Odpovídá na Martin Petrovaj
Matěj K:1. dubna 16:27

Díky moc za skvely popis:) vsechno si to poradne proctu a vyzkousim:)

 
Odpovědět 1. dubna 16:27
Avatar
Martin Petrovaj
Překladatel
Avatar
Martin Petrovaj:1. dubna 16:27

Late edit: prineskoro som si všimol, že do lambdy sa v Pythone raise vopchať nedá. Moja chyba :-D

Odpovědět 1. dubna 16:27
if (this.motto == "") { throw new NotImplementedException(); }
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 17. Zobrazit vše