Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

4. diel - Odkazy na objekty, ich kopírovanie a Garbage collector

V minulej lekcii, Hracia kocka v Pythone druhýkrát - Prekrývanie metód a random , sme sa naučili prekrývať metódy, používať vnútorný import a dokončili sme hraciu kocku.

V minulej lekcii, Hracia kocka v Pythone druhýkrát - Prekrývanie metód a random , sme si vytvorili svoj prvý poriadny objekt, bola ním hracia kocka. Začíname pracovať s objektmi. Je dôležité, aby sme presne vedeli, čo sa vo vnútri programu deje, inak by nás v budúcnosti mohlo všeličo prekvapiť. Práve na to sa zameriame v dnešnom Python tutoriálu.

Aplikácia (resp. Jej vlákno) má operačným systémom pridelenú pamäť v podobe tzv. Zásobníka (stack). Jedná sa o veľmi rýchlu pamäť s priamym prístupom, jej veľkosť aplikácie nemôže ovplyvniť, prostriedky sú prideľované operačným systémom.

Vytvorme si novú konzolovú aplikáciu a vytvorme si v nej jednoduchú triedu, ktorá bude reprezentovať užívateľa nejakého systému. Pre názornosť vypustím komentáre a nebudem riešiť viditeľnosti:

class Uzivatel:

    def __init__(self, jmeno, vek):
    self.jmeno = jmeno
    self.vek = vek

    def __str__(self):
    return str(self.jmeno)

Trieda má 2 jednoduché verejné atribúty, metódu konstruktoru a __str __ (), aby sme mohli používateľa jednoducho vypisovať. Do programu pridajme vytvorení inštancie tejto triedy:

u = Uzivatel("Jan Novák", 28)

Premenná u obsahuje odkaz na objekt. Pozrime sa na situáciu v pamäti:

Zásobník a halda v pamäti počítača - Objektovo orientované programovanie v Pythone

Zásobník aj halda sa nachádzajú v pamäti RAM. Rozdiel je v prístupe a veľkosti. Halda je prakticky neobmedzená pamäť, ku ktorej je však prístup zložitejšia a tým pádom pomalší. Naopak zásobník je pamäť rýchla, ale veľkostne obmedzená.

Objekty sú v pamäti uložené vlastne na dvakrát, raz v zásobníku a raz v halde. V zásobníku je uložená iba tzv. Referencie, teda odkaz do haldy, kde sa potom nachádza naozajstný objekt.

Môžete sa pýtať, prečo je to takto urobené. Dôvodov je hneď niekoľko, poďme si niektoré vymenovať:

  1. Miesto vo stacku je obmedzené.
  2. Keď budeme chcieť použiť objekt viackrát (napr. Ho odovzdať ako parameter do niekoľkých metód), nemusíme ho v programe odovzdávať ako kópiu. Odovzdáme iba referenciu na objekt namiesto toho, aby sme všeobecne pamäťovo náročný objekt kopírovali. Toto si vzápätí ukážeme.

Založme si 2 premenné typu Uzivatel:

u = Uzivatel("Jan Novák", 28)
v = Uzivatel("Josef Nový", 32)

Situácia v pamäti bude nasledovné:

Odkazy na objekt v Pythone v pamäti počítača - Objektovo orientované programovanie v Pythone

Teraz skúsme priradiť do premennej u premennú v. Pri objekte sa skopíruje iba odkaz na objekt, ale objekt máme stále len jeden. V kóde vykonáme teda toto:

u = Uzivatel("Jan Novák", 28)
v = Uzivatel("Josef Nový", 32)
u = v

V pamäti bude celá situácia vyzerať nasledovne:

Odkazy na objekt v pamäti počítača - Objektovo orientované programovanie v Pythone

Presvedčte sa o tom, aby ste videli, že to naozaj tak je :) Najprv si necháme obe dve premenné vypísať pred a po zmene. Navyše si vypíšeme aj id objektov cez funkciu id (). Pretože budeme výpis volať viackrát, napíšem ho trochu úspornejšie. Upravme teda kód na nasledujúce:

# založení proměnných
u = Uzivatel("Jan Novák", 28)
v = Uzivatel("Josef Nový", 32)
print("u: {0}\nv: {1}".format(u, v))
print("u: {0}\nv: {1}\n".format(id(u), id(v)))
# přiřazování
u = v
print("u: {0}\nv: {1}".format(u, v))
print("u: {0}\nv: {1}\n".format(id(u), id(v)))
input()

Výstup programu:

Používatelia ako odkazy na objekt v Pythone - Objektovo orientované programovanie v Pythone

Poďme zmeniť meno používateľa v a podľa našich predpokladov by sa mala zmena prejaviť aj v premennej u. K programu pripíšeme:

# změna
v.jmeno = "John Doe"
print("u: {0}\nv: {1}".format(u, v))
print("u: {0}\nv: {1}\n".format(id(u), id(v)))

Zmenili sme objekt v premennej v a znovu vypíšeme u a v:

Používatelia ako odkazy na objekt v Pythone - Objektovo orientované programovanie v Pythone

Spolu so zmenou v sa zmení aj u, pretože premenné ukazujú na ten istý objekt. Pripomeňme si situáciu v pamäti ešte raz a zamerajme sa na Jána Nováka.

Používatelia ako odkazy na objekt v Pythone - Objektovo orientované programovanie v Pythone

Čo sa sním stane? "Zožerie" ho tzv. Garbage collector.

garbage collector - Objektovo orientované programovanie v Pythone

Garbage collector a dynamická správa pamäte

Pamäť môžeme v programoch alokovať staticky, to znamená, že v zdrojovom kóde vopred určíme, koľko jej budeme používať. Ale tiež nemusíme určiť koľko pamäte budeme potrebovať. V tomto prípade hovoríme o dynamickej správe pamäte.

V minulosti, hlavne v časoch jazykov C, Pascal a C ++, sa na tento účel používali tzv. Pointer, čiže priame ukazovatele do pamäte. Napospol to fungovalo tak, že sme si povedali operačnému systému o kus pamäti o určitej veľkosti. On ju pre nás vyhradil a dal nám jej adresu. Na toto miesto v pamäti sme mali pointer, cez ktorý sme s pamäťou pracovali. Problém bol, že nikto nestrážil, čo do pamäti dávame (ukazovateľ smeroval na začiatok vyhradeného priestoru). Keď sme tam dali niečo väčšieho, skrátka sa to rovnako uložilo a prepísala sa dáta za naším priestorom, ktorá patrila napríklad inému programu alebo operačnému systému (v tom prípade by našu aplikáciu OS asi zabil - zastavil). Často sme si však my v pamäti prepísali nejaká ďalšie dáta nášho programu a program sa začal správať chaoticky. Predstavte si, že si uložíte používateľa do poľa a v tej chvíli sa vám zrazu zmení farba užívateľského prostredia, teda niečo, čo s tým vôbec nesúvisí. Hodiny strávite tým, že kontrolujete kód pre zmenu farby, potom zistíte, že je chyba v založenia používateľa, kedy dôjde k pretečeniu pamäte a prepísanie hodnôt farby.

Keď naopak nejaký objekt prestaneme používať, musíme po ňom miesto sami uvoľniť, ak to neurobíme, pamäť zostane blokovaná. Pokiaľ toto robíme napr. V nejakej metóde a zabudneme pamäť uvoľňovať, naše aplikácie začne padať, prípadne zasekne celý operačný systém. Takáto chyba sa opäť zle hľadá, prečo program prestane po niekoľkých hodinách fungovať? Kde tú chybu v niekoľkých tisícoch riadkov kódu vôbec hľadať? Nemáme jedinú stopu, nemôžeme sa ničoho chytiť, musíme prejsť celý program riadok po riadku. Brrr. Podobný problém nastane, keď si niekde pamäť uvoľníme a následne pointer opäť použijeme (zabudneme, že je uvoľnený, to sa môže ľahko stať), povedie niekam, kde je už uloženého niečo iné a tieto dáta budú opäť prepísané. Povedie to k nekontrolovateľnému správanie našej aplikácie a môže to dopadnúť aj takto:

Blue Screen Of Death – BSOD vo Windows - Objektovo orientované programovanie v Pythone

Až na malú skupinu géniov ľudí prestalo baviť riešiť neustále a nezmyselné chyby. Za cenu mierneho zníženia výkonu vznikli riadené jazyky (managed) s tzv. Garbage collector, jedným z nich je aj Python. C ++ sa samozrejme naďalej používa, ale len na špecifické programy, napr. Časti operačného systému alebo 3D enginy komerčných hier, kde je potreba z počítača dostať maximálny výkon. Na 99% všetkých ostatných aplikácií sa viac hodí Python alebo iný riadený jazyk, kvôli automatické správe pamäte.

garbage collector - Objektovo orientované programovanie v Pythone
Garbage collector je vlastne program, ktorý beží paralelne s našou aplikáciou, v samostatnom vlákne. Občas sa spustí a pozrie sa, na ktoré objekty už v pamäti nevedú žiadne referencie. Tie potom odstráni. Strata výkonu je minimálna a značne to zníži percento samovrážd programátorov, ladiacich po večeroch rozbité pointera. Zapnutie a vypnutie GC môžeme dokonca z kódu ovplyvniť, aj keď to nie je v 99% prípadov vôbec potrebné. Pretože je jazyk riadený a nepracujeme s priamymi Pointer, nie je vôbec možné pamäť nejako narušiť, nechať ju pretiecť a podobne, interpret sa o pamäť automaticky stará.

Hodnota None

Posledná vec, o ktorej sa zmienime, je tzv. Hodnota None. Referenčné typy môžu, na rozdiel od hodnotových, nadobúdať špeciálne hodnoty a to None. None je kľúčové slovo a označuje, že referencie neukazuje na žiadne dáta. Keď nastavíme premennú v na None, zrušíme iba referenciu. Pokiaľ na náš objekt existuje ešte nejaká referencie, bude aj naďalej existovať. Ak nie, bude uvoľnený GC. Zmeňme ešte posledná riadky nášho programu na:

# další změna
v.jmeno = "John Doe"
v = None

výstup:

Používatelia ako odkazy na objekt v Pythone - Objektovo orientované programovanie v Pythone

Vidíme, že objekt stále existuje a ukazuje na neho premenná u, v premennej v už nie je referencie. None sa využíva napr. Pri testovaní príslušnosti pomocou kľúčového slova is, ale o tom niekedy inokedy.

Kopírovanie objektov

Ak sa pýtate, ako vytvoriť naozajstnú kópiu objektu, tak môžeme objekt znova vytvoriť pomocou konstruktoru a dať do neho rovnaké dáta, alebo môžeme použiť hlbokú kópiu. Vrátime sa k našej kocke z minulého dielu:

class Kostka:
    """
    Třída reprezentuje hrací kostku.
    """

    def __init__(self, pocet_sten=6):
        self.__pocet_sten = pocet_sten

    def __str__(self):
        """
        Vrací textovou reprezentaci kostky.
        """
        return str("Kostka s {0} stěnami".format(self.__pocet_sten))

    def vrat_pocet_sten(self):
        return self.__pocet_sten

    def hod(self):
        """
        Vykoná hod kostkou a vrátí číslo od 1 do
        počtu stěn.
        """
        import random as _random
        return _random.randint(1, self.__pocet_sten)

Do kocky pridáme metódu podobnú metóde __str __ () a to metódu __repr __ (). Táto metóda tiež vracia reťazec, ktorý môžeme potom odovzdať funkciu eval (). Táto funkcia sa používa k dynamickému vykonávanie kódu. Ovšem nechávať nášho užívateľa zadať výraz je dosť nebezpečné. Naša metóda __repr __ () bude vracať konštruktor kocky:

def __repr__(self):
    """
    Vrací v řetězci kód konstruktoru pro funkci eval().
    """
    return str("Kostka({0})".format(self.__pocet_sten))

Kód pre vytvorenie novej kocky:

jina_sestistenna = eval(repr(sestistenna))

Alebo môžeme použiť modul copy. A z neho funkciu deepcopy ():

jina_sestistenna = copy.deepcopy(sestistenna)

V budúcej lekcii, Riešené úlohy k 4. lekcii OOP v Pythone , si zase niečo praktické naprogramujeme, nech si vedomosti zažijeme. Prezradím, že pôjde o objekt bojovníka do našej arény. To je zatiaľ všetko :)

V nasledujúcom cvičení, Riešené úlohy k 4. lekcii OOP v Pythone, si precvičíme 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é 491x (2.42 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Python

 

Predchádzajúci článok
Hracia kocka v Pythone druhýkrát - Prekrývanie metód a random
Všetky články v sekcii
Objektovo orientované programovanie v Pythone
Preskočiť článok
(neodporúčame)
Riešené úlohy k 4. lekcii OOP v Pythone
Článok pre vás napísal gcx11
Avatar
Užívateľské hodnotenie:
1 hlasov
(^_^)
Aktivity