Zarábaj až 6 000 € mesačne! Akreditované rekvalifikačné kurzy od 0 €. Viac informácií.

18. diel - Vlastnosti v Pythone

V predchádzajúcej lekcii, Dekorátory druhýkrát - Parametrické a triedne dekorátory, sme dokončili tému s dekorátormi.

V dnešnom tutoriáli objektovo orientovaného programovania v Pythone sa pozrieme na ďalšie prvky tried, ktoré ešte nepoznáme. Zameriame sa na vlastnosti (properties). Ukážeme si, ako vďaka nim elegantne vyriešime nastavovanie a validáciu hodnôt atribútov.

Vlastnosti v Pythone

Vlastnosti nám ponúkajú elegantný a flexibilný spôsob, ako pracovať s atribútmi objektov. Vo svojej podstate nám umožňujú definovať metódy, ktoré vyzerajú a chovajú sa ako atribúty. Vďaka tomu máme pod kontrolou, čo sa stane, keď je atribút nastavený, čítaný, alebo dokonca mazaný, a to všetko s uchovaním rovnakého rozhrania ako pri obyčajných atribútoch. To je užitočné v kontexte zapuzdrenia dát a obmedzenia priameho prístupu k dátam objektu, hoci rozhodne nejde o absolútne riešenie. V mnohých iných jazykoch je zapuzdrenie implementované pomocou súkromných atribútov a verejných metód (getters a setters). My už tušíme, že Python má ako obvykle svoj vlastný špecifický prístup. Ponúka nám vlastnosti, ktoré tieto koncepty spájajú do jedného elegantného a čitateľného riešenia.

Dekorátor @property

Kľúčovým prvkom pre vytvorenie vlastnosti je dekorátor @property. Ak máme jednoduchý atribút, ktorý chceme upraviť tak, aby obsahoval validáciu alebo dodatočnú logiku pri čítaní, práve týmto dekorátorom ho premeníme na vlastnosť.

Majme napríklad triedu Person s atribútom age. Chceme zaistiť, že vek bude vždy kladné číslo. Preto tento atribút premeníme na vlastnosť a pridáme zodpovedajúcu validáciu:

Klikni pre editáciu
  • class Person:
        def __init__(self, age):
            self._age = age          # private attribute age
    
        @property
        def age(self):
            return f"The age of the person is {self._age} years."
    
        @age.setter
        def age(self, value):
            if value < 0:
                print("Error! Age cannot be negative.")
            else:
                self._age = value
    
    person = Person(10)
    person.age = -20
    print(person.age)
    • Skontroluj, či výstupy programu zodpovedajú predlohe. S inými textami testy neprejdú.

    V tomto príklade je _age skrytý atribút, ktorý skutočne uchováva hodnotu veku. Verejná vlastnosť age (už bez podčiarkovníka) potom umožňuje čítať a nastavovať tento skrytý atribút, ale s možnosťou pridania ďalšej logiky, ako je validácia. Pokus o vloženie nesprávnej hodnoty do atribútu spôsobí vypísanie chybovej hlášky a zmena sa nevykoná.

    Atribút je stále priamo dostupný aj zvonku, pokiaľ k nemu pristúpime zápisom person._age = -20. Pravidlá prístupu k atribútom zostávajú rovnaké, ako sme si ich ukázali v lekcii Zapúzdrenie atribútov podrobne.

    Kombinácia @property a @attribute_name.setter je syntaktická povinnosť, nie iba konvencia. Zatiaľ čo @property bez setteru použiť je možné, pokus použiť setter bez @property spôsobí chybu. Pokiaľ použijeme iba @property (hovorí sa mu tiež getter), získame takzvanú read-only vlastnosť. To znamená, že hodnotu tejto vlastnosti možno získať (prečítať), ale nemôže byť priamo nastavená.

    Pomocou @property, teda getteru, dáta čítame a pomocou @attribute_name.setter, teda setteru, dáta nastavujeme.

    Kedy použiť vlastnosti namiesto metód

    Vlastnosti v Pythone nám umožňujú kombinovať výhody metód a prístupu k atribútom. Ponúka sa otázka, kedy je vhodné vlastnosti použiť namiesto klasických metód. Pozrime sa na niekoľko situácií, kedy sú vlastnosti vhodnou voľbou:

    • prirodzený prístup k dátam – ak chceme, aby sa prístup k niektorým dátam triedy zdal byť ako k obyčajnému atribútu, ale potrebujeme viac kontroly nad tým, čo sa deje pri čítaní alebo zápise týchto dát,
    • zachovanie kompatibility – ak máme existujúcu triedu s verejným atribútom a chceme pridať nejakú logiku pri čítaní alebo zápise tohto atribútu, zmeníme ho na vlastnosť bez nutnosti meniť rozhranie triedy. To znamená, že existujúci kód, ktorý túto triedu využíva, bude fungovať bez zmien,
    • validácia dát – vlastnosti sú ideálne na implementáciu validácie dát.

    Vzhľadom na princípy, na ktorých je Python postavený, vlastnosti neposkytujú z hľadiska zapuzdrenia príliš robustnú ochranu. Stále je našou zodpovednosťou vyvarovať sa priamym zásahom do súkromných alebo privátnych atribútov.

    Pozrime sa na doterajší výklad formou komplexnejšieho príkladu. Predstavme si, že máme triedu Regulator, ktorá simuluje teplotný regulátor. Chceme, aby užívateľ mohol nastaviť požadovanú teplotu, ale zároveň chceme zaistiť, že táto teplota nebude príliš vysoká ani príliš nízka (napr. medzi 10 °C a 30 °C):

    Klikni pre editáciu
    • class Regulator:
          def __init__(self):
              self._temperature = 20  # default temperature
      
          @property
          def temperature(self):
              return f"The current temperature is set to {self._temperature} °C."
      
          @temperature.setter
          def temperature(self, new_temperature):
              if 10 <= new_temperature <= 30:
                  self._temperature = new_temperature
              else:
                  print(f"Error! The temperature {new_temperature} °C is outside the allowed range (10 °C - 30 °C).")
      
          def set_temperature_in_kelvin(self, temperature_k):
              temperature_c = temperature_k - 273.15
              if 10 <= temperature_c <= 30:
                  self._temperature = temperature_c
                  return f"The current temperature is set to {temperature_k} °K."
              else:
                  return f"Error! The temperature {temperature_k} K is outside the allowed range in °C (10 °C - 30 °C)."
      
      # Usage:
      regulator = Regulator()
      print(regulator.temperature)  # Displays: The current temperature is set to 20 °C.
      
      regulator.temperature = 25
      print(regulator.temperature)
      
      regulator.temperature = 35    # Displays error, temperature remains at 25 °C
      print(regulator.temperature)
      
      print(regulator.set_temperature_in_kelvin(298.15))
      • Skontroluj, či výstupy programu zodpovedajú predlohe. S inými textami testy neprejdú.

      V tomto rozšírenom príklade používateľ nastavuje teplotu v °C pomocou vlastnosti @temperature.setter, ale má tiež možnosť nastaviť teplotu v kelvinoch pomocou metódy set_temperature_in_kelvin(). V praxi to znamená , že ak existuje nejaká špecifická funkcia alebo spôsob manipulácie s dátami, ktoré sa často nepoužívajú alebo sú zložitejšie, je lepšie použiť tradičnú metódu.

      Pokiaľ však chceme, aby sa niektoré často vykonávané akcie (napr. validácia) vykonávali automaticky pri čítaní alebo nastavovaní hodnoty, sú vlastnosti ideálnym riešením.

      Vlastnosť deleter

      Vlastností v Pythone je možné nielen čítať a nastavovať, ale aj mazať. Na tento účel nám Python ponúka možnosť definovať metódu, ktorá sa vyvolá, keď s našou vlastnosťou použijeme kľúčové slovo del. Túto metódu označujeme pomocou dekorátora @<property_name>.deleter.

      Vytvorenie vlastnosti deleter

      Predstavme si nasledujúcu situáciu. Máme triedu, ktorá reprezentuje dokument. Po zmazaní vlastnosti content chceme vymazať aj hodnotu atribútu _content, ktorý zastupovala, a nastaviť vlastnosť deleted na True:

      Klikni pre editáciu
      • class Document:
            def __init__(self, content):
                self._content = content
                self.deleted = False
        
            @property
            def content(self):
                if not self.deleted:
                    return self._content
                return "The document has been deleted."
        
            @content.deleter
            def content(self):
                self._content = None
                self.deleted = True
                print("The document has been deleted.")
        
        # Usage:
        doc = Document("This is the content of my document.")
        print(doc.content)  #  Displays: This is the content of my document.
        
        del doc.content     # Displays: The document has been deleted.
        print(doc.deleted)  # Displays: True
        • Skontroluj, či výstupy programu zodpovedajú predlohe. S inými textami testy neprejdú.

        Zatiaľ čo setter je užitočný pre validáciu hodnôt alebo pre spustenie určitej logiky po nastavení hodnoty, deleter použijeme, ak chceme:

        • uvoľniť prostriedky – ak naša trieda spravuje prostriedky, ako sú súbory alebo sieťové spojenia, deleter využijeme na ich uvoľnenie,
        • aktualizovať súvisiace atribúty – rovnako ako v našom príklade s dokumentom môže byť potrebné aktualizovať niektoré súvisiace atribúty.

        Deleter je v praxi oveľa menej bežný ako gettery a settery. Mnoho programátorov v Pythone ho nikdy nepoužije. Je to preto, že v mnohých prípadoch nie je potrebné explicitne ručiť za mazanie atribútov alebo ich prostriedkov. Ak však máme konkrétny dôvod na jeho použitie, je dobré vedieť, že túto možnosť máme k dispozícii.

        V nasledujúcej lekcii, Vlastnosti v Pythone - Pokročilé vlastnosti a dedenie, sa v práci s vlastnosťami zameriame na dedenie, časté chyby a vytváranie vlastných dekorátorov pre vlastnosti.


         

        Ako sa ti páči článok?
        Pred uložením hodnotenia, popíš prosím autorovi, čo je zleZnakov 0 z 50-500
        Predchádzajúci článok
        Dekorátory druhýkrát - Parametrické a triedne dekorátory
        Všetky články v sekcii
        Objektovo orientované programovanie v Pythone
        Preskočiť článok
        (neodporúčame)
        Vlastnosti v Pythone - Pokročilé vlastnosti a dedenie
        Článok pre vás napísal gcx11
        Avatar
        Užívateľské hodnotenie:
        10 hlasov
        (^_^)
        Aktivity