20. diel - Magické metódy v Pythone
V predchádzajúcej lekcii, Vlastnosti v Pythone - Pokročilé vlastnosti a dedenie, sme sa v práci s vlastnosťami zamerali na dedenie, časté chyby a vytváranie vlastných dekorátorov pre vlastnosti.
V nasledujúcom tutoriáli objektovo orientovaného programovania v Pythone sa zameriame na magické (dunder) metódy objektov. Znalosť dunder metód nám poskytne hlbšie pochopenie toho, ako Python funguje "pod kapotou". Vďaka tomu budeme schopní lepšie rozumieť správaniu objektov a ich interakciám s jazykovými konštruktmi.
Magické metódy
Magické (dunder) metódy sú kľúčovou súčasťou Pythona a tvoria
základ mnohých jeho OOP funkcií. Názov "dunder" pochádza z anglického
"double underscore" (dvojité podčiarkovníky), čo odkazuje na ich typický
zápis, napríklad __init__()
alebo __str__()
. Tieto
metódy boli do Pythona pridané, aby umožnili vývojárom definovať
správanie objektov v rôznych kontextoch, ako je inicializácia,
reprezentácia, aritmetické operácie a mnoho ďalších.
S niekoľkými týmito metódami sme sa už stretli, ako napríklad s
magickou metódou __init__()
na inicializáciu objektu alebo s
metódou __str__()
na vypísanie jeho ľudsky čitateľnej
reprezentácie.
Prehľad magických dunder metód
Dunder metódy si pre lepší prehľad rozdelíme do niekoľkých kategórií podľa ich funkcií:
1. Základné dunder metódy
- objektová inicializácia a reprezentácia -
__init__()
,__str__()
,__repr__()
, - operátory porovnanie -
__eq__()
,__lt__()
,__gt__()
, a ďalšie, - logické operácie -
__bool__()
- matematické operátory -
__add__()
,__sub__()
,__mul__()
,__truediv__()
, a ďalšie, - správa kontextu -
__enter__()
,__exit__()
(pre použitie vowith
príkaze).
2. Pokročilé dunder metódy
- atribútové a indexové operácie -
__getattr__()
,__setattr__()
,__delattr__()
,__getitem__()
,__setitem__()
, - volanie objektov -
__call__()
(premena objektu na volateľný objekt), - správa inštancií -
__new__()
(používa sa na tvorbu nových inštancií).
3. Špeciálne prípady
- vytvorenie užívateľsky definovaných kontajnerov -
__len__()
,__getitem__()
, - custom iterátory a generátory -
__iter__()
a__next__()
, - správa pamäte a Garbage Collection -
__del__()
.
Toto je len základný prehľad. V tejto lekcii sa budeme venovať detailne prvej kategórii. Vopred upozorníme, že mnoho dunder metód je hlboko prepojených s kolekciami, ktoré zatiaľ nepoznáme. Zoznámime sa s nimi v kurze Kolekcie v Pythone. Napriek tomu sa v príkladoch kolekciám vyhneme, aby boli čo najviac pochopiteľné. Poďme na to.
Základné dunder metódy
Začneme prehľadom a príklady použitia základných metód.
Objektová inicializácia a reprezentácia
Tieto metódy, ktoré už dobre poznáme, sú základným stavebným kameňom pre akúkoľvek triedu v Pythone.
Metódy __init__()
,
__str__()
a __repr__()
Metódu __init__()
Python volá, kedykoľvek vytvárame novú
inštanciu triedy. Používa sa na inicializáciu atribútov. Metódy
__str__()
a __repr__()
určujú, ako bude objekt
reprezentovaný ako reťazec. Zatiaľ čo __str__()
vracia
čitateľnú reprezentáciu pre koncových užívateľov,
__repr__()
má za cieľ poskytnúť jednoznačnú reprezentáciu
objektu, ktorá je často technickej povahy a vhodná pre vývojárov. Tu si
príklady uvádzať nebudeme, so všetkými spomínanými metódami sme sa už
detailne zoznámili skôr.
Operátory porovnanie
Na porovnávacie metódy sa pozrime vo forme tabuľky:
Metóda | Slovný popis | Reprezentuje kód |
---|---|---|
__lt__(self, other) |
menšie ako (less than) | x < y |
__le__(self, other) |
menší alebo rovný (less or equal) | x <= y |
__eq__(self, other) |
rovná sa (equal) | x == y |
__ne__(self, other) |
nerovná sa (not equal) | x != y |
__gt__(self, other) |
väčšia ako (greater than) | x > y |
__ge__(self, other) |
väčší alebo rovný (greater or equal) | x >= y |
Všetky porovnávacie metódy vracajú True
alebo
False
, popr. vyvolajú výnimku, ak sa porovnávanie s druhým
objektom nepodporuje. Ich syntax je rovnaká:
class Number: def __init__(self, value): self.value = value def __gt__(self, other_number): if isinstance(other_number, Number): return self.value > other_number.value else: raise ValueError("Cannot compare 'Number' with an object of a different type.") pi = Number(3.14) e = Number(2.74) print(pi > e) # calls pi.__gt__(e), where e is "other" # Example of an exception when trying to compare 'Number' with 'str' other_value = "text" try: print(pi > other_value) # raises an exception except ValueError as error: print(error) # prints the error message
Skontroluj, či výstupy programu zodpovedajú predlohe. S inými textami testy neprejdú.
Logické operácie
Metóda pre logické operácie určuje, ako sa má objekt správať v kontexte pravdivostných testov a logických operácií.
Metóda __bool__()
Pozrime sa, ako dokážeme prispôsobiť správanie metódy pri prevode
objektu na typ bool
:
class Car: def __init__(self, fuel): self.fuel = fuel def __bool__(self): # The car is "True" (functional) if it has more than 10 liters of fuel return self.fuel > 10 # Testing service_car = Car(5) # Not enough fuel my_car = Car(20) # Enough fuel print(bool(service_car)) # Prints False print(bool(my_car)) # Prints True
Skontroluj, či výstupy programu zodpovedajú predlohe. S inými textami testy neprejdú.
V príklade metóda __bool__()
kontroluje, či je v nádrži
viac ako 10
litrov paliva. Ak áno, vracia True
, inak
False
. Tým je dosiahnuté to, že hodnota bool()
na
inštancii triedy Car
priamo závisí od množstva paliva v
nádrži.
Tento prístup je užitočný v situáciách, keď chceme, aby hodnota
bool()
objektu reprezentovala jeho stav podľa nejakého
špecifického kritéria, ktoré je dôležité pre logiku nášho programu.
Matematické operátory
Matematické operátory v Pythone sú skvelým príkladom toho, ako dunder metódy umožňujú objektom reagovať na štandardné matematické operácie. Pozrieme sa na ne opäť vo forme tabuľky:
Metóda | Slovný popis | Reprezentuje kód |
---|---|---|
__add__(self, other) |
sčítanie | + |
__sub__(self, other) |
odčítanie | - |
__mul__(self, other) |
násobenie | * |
__truediv__(self, other) |
delenie | / |
__eq__(self, other) |
rovná sa | = |
__mod__(self, other) |
modulo | % |
__pow__(self, other[, modulo]) |
umocňovanie | ** |
Tieto metódy umožňujú objektom pracovať s aritmetickými operátormi
rovnako, ako by to boli bežné číselné typy. Ako príklad si definujeme
triedu ComplexNumber
, ktorá bude reprezentovať komplexné čísla
a bude implementovať súčet:
class ComplexNumber: def __init__(self, real, imaginary): self.real = real self.imaginary = imaginary def __add__(self, other): return ComplexNumber(self.real + other.real, self.imaginary + other.imaginary) def __str__(self): return f"{self.real} + {self.imaginary}i" # Testing first_complex = ComplexNumber(1, 2) second_complex = ComplexNumber(3, 4) result = first_complex + second_complex print(result) # Prints "4 + 6i"
Skontroluj, či výstupy programu zodpovedajú predlohe. S inými textami testy neprejdú.
Všetky spomenuté metódy umožňujú preťažiť správanie štandardných operátorov a prispôsobiť ich funkčnosť pre užívateľsky definované objekty.
Preťaženie operátorov
Python nám umožňuje modifikovať spôsob, akým štandardné operátory
(ako +
, -
, *
, /
,
<
, ==
a ďalšie) fungujú s našimi vlastnými
objektmi. Už sme si ukázali, že pre každý operátor existuje zodpovedajúca
metóda. Teraz si ukážeme, ako ju definovať v rámci triedy, aby sa zmenila
štandardná funkcionalita daného operátora.
Vytvoríme si triedu Vector
, ktorá reprezentuje 2D vektor a
preťažuje niektorých matematických operátorov:
class Vector: def __init__(self, x_coordinate, y_coordinate): self.x_coordinate = x_coordinate self.y_coordinate = y_coordinate def __repr__(self): return f"Vector({self.x_coordinate}, {self.y_coordinate})" def __add__(self, other_vector): # Overloading the + operator return Vector(self.x_coordinate + other_vector.x_coordinate, self.y_coordinate + other_vector.y_coordinate) def __sub__(self, other_vector): # Overloading the - operator return Vector(self.x_coordinate - other_vector.x_coordinate, self.y_coordinate - other_vector.y_coordinate) def __mul__(self, scalar_value): # Overloading the * operator for scalar multiplication return Vector(self.x_coordinate * scalar_value, self.y_coordinate * scalar_value) # Example of usage vector1 = Vector(2, 3) vector2 = Vector(1, 1) print(vector1 + vector2) # Output: Vector(3, 4) print(vector1 - vector2) # Output: Vector(1, 2) print(vector1 * 3) # Output: Vector(6, 9)
Skontroluj, či výstupy programu zodpovedajú predlohe. S inými textami testy neprejdú.
Správa kontextu
V Pythone sa správa kontextu využíva hlavne na správne spracovanie
zdrojov a na zabezpečenie, že sú tieto zdroje korektne uvoľnené alebo
uzavreté po použití. Kľúčovým prvkom pre správu kontextu je využitie
príkazu with
, ktorý automaticky zaistí správne otvorenie a
uzavretie zdrojov, ako sú súbory, sieťové pripojenia a podobne. Táto
kapitola je zatiaľ trochu mimo našich obzorov, súbory a sietí sa budeme
zaoberať v pokročilejších kurzoch. Napriek tomu vo výčte nesmie chýbať a
príklad, ktorý si uvedieme, bude úplne v medziach našich znalostí.
Metóda __enter__()
Metóda __enter__()
sa zavolá v okamihu, keď kód vstupuje do
kontextového manažéra – teda do bloku kódu definovaného príkazom
with
. Typicky sa v tejto metóde vykonávajú inicializačné
operácie, ako je otvorenie súboru alebo získanie zámku.
Metóda __exit__()
Metóda __exit__()
sa zavolá na konci bloku kódu v príkaze
with
, bez ohľadu na to, či bol blok opustený normálne, alebo
došlo k vyhodeniu výnimky. Táto metóda zvyčajne zahŕňa upratovacie
operácie, ako je zatváranie súborov alebo uvoľnenie zdrojov. Má tri
argumenty, ktoré reprezentujú typ výnimky, hodnotu výnimky a traceback, ak
došlo k vyhodeniu výnimky.
Pozrime sa na príklad:
import time class Timer: def __enter__(self): self.start = time.time() return self def __exit__(self, exc_type, exc_val, traceback): self.end = time.time() print(f"Duration: {self.end - self.start} seconds.") # Usage: with Timer() as t: # Code we want to measure: for i in range(1000000): i *= 2 # Upon exiting the 'with' block, the duration is printed
Skontroluj, či výstupy programu zodpovedajú predlohe. S inými textami testy neprejdú.
Rozhodne nejde o kompletný zoznam všetkých dunder metód v daných
kategóriách. To by jednak lekcia extrémne nadobudla na objeme a tiež by sa
podobala skôr encyklopédii. Pre hlbšie štúdium je tu dokumentácia
Pythona, kde je zoznam naozaj komplexný. To je pre túto lekciu všetko
V budúcej lekcii, Magické metódy v Pythone druhýkrát, sa pozrieme na ďalšie, pokročilejšie magické metódy objektov.