Body zdarma Body zdarma
Využij podzimních slev a získej od nás až 40 % bodů zdarma! Více zde

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

Unicorn College 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.

Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

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 292x (545 B)
Aplikace je včetně zdrojových kódů v jazyce python

 

 

Aktivity (2)

 

 

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

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.2.2018 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.2.2018 12:57
Avatar
gcx11
Redaktor
Avatar
Odpovídá na brevnovak
gcx11:22.2.2018 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.2.2018 19:46
Avatar
Matěj K
Člen
Avatar
Matěj K:1.4.2018 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.4.2018 14:47
Avatar
Martin Petrovaj
Překladatel
Avatar
Odpovídá na Matěj K
Martin Petrovaj:1.4.2018 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.4.2018 16:20
Odpovědět 1.4.2018 16:19
if (this.motto == "") { throw new NotImplementedException(); }
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Matěj K
Člen
Avatar
Odpovídá na Martin Petrovaj
Matěj K:1.4.2018 16:27

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

 
Odpovědět 1.4.2018 16:27
Avatar
Martin Petrovaj
Překladatel
Avatar
Martin Petrovaj:1.4.2018 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.4.2018 16:27
if (this.motto == "") { throw new NotImplementedException(); }
Avatar
Filip Něnička:4. dubna 20:33

Ahoj.
Nevíte proč mi nejde importovat modul? ModulNotFoundError. Umístil jsem soubor do adresáře s konzolí, ale pořád nejede. Děkuji za radu.

Filip

 
Odpovědět 4. dubna 20:33
Avatar
Rudolf Kov
Člen
Avatar
Rudolf Kov:8. června 21:21

Ahoj, můžu se zeptat, proč mi takhle napsaná kalkučka místo výsledku napíše "function Poctari.secti at 0x02FFE1E0>" ?

class Poctari:
def secti(a, b):
return a + b
def vynasob(a, b):
return a * b
def odecti(a, b):
return a - b
def vydel(a, b):
return a / b
poctar = Poctari

funkce = int(input("Jakou operaci chcete provést? Pro sčítání stiskněte 1, pro násobení 2, pro odčítání 3 a pro dělení 4. Děkujeme"))

poctar.a = int(input("Zadejte 1. číslo"))
poctar.b = int(input("Zadejte 2. číslo"))

if funkce == 1:
print(poctar.secti)
elif funkce == 2:
print(poctar.vy­nasob)
elif funkce == 3:
print(poctar.o­decti)
elif funkce == 4:
print(poctar.vydel)
else:
print("chyba")

 
Odpovědět 8. června 21:21
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Rudolf Kov
Jindřich Máca:14. června 10:57

Ahoj, nejspíš proto, že to máš špatně. :D Ale vážně, máš tam docela hodně chyb v základních věcech, takže bych Ti vřele doporučil projít si poctivě celý zdejší seriál na Python.

Funkční řešení by mohlo vypadat např. takto:

class Poctari:
    # Počáteční definice hodnot
    a = 0
    b = 0

    # Počítání s hodnotami (atributy) uloženými uvnitř třídy

    def secti(self): # U metod je důležité uvádět definici self
        return self.a + self.b

    def vynasob(self):
        return self.a * self.b

    def odecti(self):
        return self.a - self.b

    def vydel(self):
        return self.a / self.b


poctar = Poctari() # Instance třídy se píše se závorkami

funkce = int(input("Jakou operaci chcete provést? Pro sčítání stiskněte 1, pro násobení 2, pro odčítání 3 a pro dělení 4. Děkujeme"))

poctar.a = int(input("Zadejte 1. číslo"))
poctar.b = int(input("Zadejte 2. číslo"))

# Volání metod se také píše se závorkami, stejně jako volání funkcí
if funkce == 1:
    print(poctar.secti())
elif funkce == 2:
    print(poctar.vynasob())
elif funkce == 3:
    print(poctar.odecti())
elif funkce == 4:
    print(poctar.vydel())
else:
    print("chyba")
 
Odpovědět 14. června 10:57
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 20. Zobrazit vše