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í.

13. diel - Magické metódy v Pythone

V predchádzajúcom cvičení, Riešené úlohy k 10-11. lekciu Python, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.

V minulej lekcii, Riešené úlohy k 10-11. lekciu Python , sme si uviedli vlastnosti. V tomto Python tutoriálu sa pozrieme na magické metódy objektov.

Magické metódy

Magické metódy objektov sú také metódy, ktoré začínajú a končia dvoma podtržníkmi. S niekoľkými týmito metódami sme sa už stretli, ako napríklad s magickou metódou __init__ pre inicializáciu objektu alebo s metódou __str__ pre vypísanie jeho ľudsky čitateľné reprezentácie.

Vytváranie objektov

__new __ (cls, * args, ** kwargs)

Metódu __new__ voláme, keď potrebujeme kontrolu vytvárania objektu. Najmä ak máme vlastnú triedu, ktorá dedí od vstavaných tried ako napríklad int (číslo) alebo str (reťazec). Niekedy je lepšie pre danú situáciu použiť deskriptory alebo návrhový vzor Factory.

Metóda __new__ vracia buď vytvorený objekt alebo nič. Ak objekt vráti, tak sa zavolá metóda init, ak nie, tak sa metóda __init__ nevolá.

Ukážka

class Test:

    def __new__(cls, fail=False):
        print("Zavolána metoda __new__")
        if not fail:
            return super().__new__(cls)

    def __init__(self):
        print("Zavolána metoda __init__")

test_1 = Test()
test_2 = Test(fail=True)

Metóda __new__ berie ako prvý paramatert triedu daného objektu a potom ďalšie argumenty odovzdané v konstruktoru. Trieda ako parameter sa do metódy __new__ pridáva automaticky. Ak tvorba objektu prebehne úspešne, tak sa aj metóda __init__ volá s parametrami z konstruktoru.

V metóde __new__ môžeme dokonca už priraďovať atribúty k objektu.

Ukážka

class Point:

    def __new__(cls, x, y):
        self = super().__new__(cls)
        self.x = x
        self.y = y
        return self


point = Point(10, 5)
print(point.x, point.y)

Objekt nevraciame ihneď, ale uložíme ho do premennej a priradíme mu atribúty.

__init __ (self, * args, ** kwargs)

Metóda __init__ sa volá pri inicializácii objektov. Ako prvý parameter berie objekt (self), ktorý je odovzdaný automaticky. Metóda __init__ by mala vracať iba None, ktorý Python sám vracia, ak metóda nemá špecifikovaný návratový typ. Ak metóda __init__ vracia niečo iné ako None, tak sa vyvolá TypeError.

Ukážka v interaktívnej konzole

>>> class Test:
...     def __self__(self): return 1
...
>>> test = Test()
Traceback (most recent call last):
    ...
TypeError: __init__() should return None, not 'int'

__del __ (self)

Metóda __del__ sa nazýva destruktor objektu a volá sa pri zničení objektu, avšak jej správanie záleží na konkrétnej implementáciou Pythone. V CPythonu sa volá, ak počet referencií na objekt klesne na nulu. Príkaz del nespôsobí priame zavolanie metódy __del__, len zníži počet referencií o jednu. Navyše nie je žiadna záruka, že sa metóda __del__ zavolá pri ukončení programu. Pre uvoľňovanie zdrojov je lepšie použiť blok try-finally alebo správcu kontexte.

Reprezentácie objektov

__repr __ (self)

Metóda by mala vracať reprezentáciu zdrojového kódu objektu ako text tak, aby platilo:

x = eval(repr(x))

__str __ (self)

Táto magická metóda by mala vracať ľudsky čitateľnú reprezentáciu objektu a mala by vracať reťazec rovnako ako metóda __repr __ ().

napríklad:

class Point:

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return "x: {0.x}, y: {0.y}".format(self)

    def __repr__(self):
        return "Point({0.x}, {0.y})".format(self)


point = Point(10, 5)
print(point)
new_point = eval(repr(point))

__bytes __ (self)

Táto metóda by mala vrátiť reprezentáciu objektu za pomoci bytov, čo znamená, že by mala vracať objekt typu bytes.

__format __ (self, format_spec)

Metóda slúži na formátovanie textové reprezentácie objektu. Je volaná metódou reťazca format (str.format ())

Na formátovanie možno použiť vstavanú funkciu format, ktorá je syntaktický cukor pre:

def format(value, format_spec):
    return value.__format__(format_spec)

Viac o formátovaní reťazcov: https://www.python.org/...ps/pep-3101/

Ukážka metódy:

class Point:

    def __init__(self, x, y):
        self.x = x
        self.y = y

    ... # vypuštení předchozích metod

    def __format__(self, format_spec):
        value = "x: {0.x}, y: {0.y}".format(self)
        return format(value, format_spec)


point = Point(10, 5)
# Zarovná řetězec na délku 12 znaků, text zarovná vpravo a začátek vyplní mezerami
print("{:>12}".format(point))

Ak sa metóda neprepíše, tak použitie metódy format vyvolá TypeError (od CPythonu 3.4)

Porovnávaciu metódy

Python dosadzuje do porovnávacích metód odkazy na porovnávanú objekty.

__lt __ (self, other)

Menšie než (less than)

x < y

__le __ (self, other)

Menšie 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äčší ako (greater than)

x > y

__ge __ (self, other)

Väčší alebo rovný (greater or equal)

x >= y

Všetky porovnávaciu metódy vracia True alebo False, popr. môžu vyvolať výnimku, ak sa porovnávanie s druhým objektom nepodporuje.

Príklad:

from math import hypot

class Point:

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __lt__(self, other):
        if isinstance(other, Point):
            return hypot(self.x, self.y) < hypot(other.x, other.y)
        raise TypeError("unordable types: {}() < {}()".format(self.__class__.__name__,
                                                            other.__class__.__name__))

    def __le__(self, other):
        if isinstance(other, Point):
            return hypot(self.x, self.y) <= hypot(other.x, other.y)
        raise TypeError("unordable types: {}() <= {}()".format(self.__class__.__name__,
                                                            other.__class__.__name__))

    ...

Špeciálna metóda __class__ uchováva odkaz na triedu objektu. V ukážke sú uvedené len implementácia metód __lt__ a __le__, zvyšok je podobný. Dva objekty typu Point porovnáme podľa vzdialenosti od počiatku súradníc (tj. Bod [0, 0]).

Modul functools ponúka automatické vygenerovanie ostatných metód za pomoci jednej z metód __lt __ (), __lg __ (), __gt __ () alebo __ge __ () a trieda by mala mať aj metódu __eq __ (). Avšak použitie dekorátoru total_ordering môže mať negatívny účinok na rýchlosť programu.

Vzhľadom na to, že metóda __class __ () obsahuje odkaz na triedu, tak za pomoci nej možno objekt "naklonovať".

class Point:

    def __init__(self, x, y):
        self.x = x
        self.y = y

    ...

    def clone(self):
        return self.__class__(self.x, self.y)


point = Point(10, 5)
point_clone = point.clone()

Ďalšie metódy

__hash __ (self)

Metóda hash by mala byť reimplementována, ak je definovaná metóda __eq __ (), aby bolo možné použiť objekt v niektorých kolekcia.

Jej implementáciou:

class Point:

    ...

    def __hash__(self):
        return hash(id(self))

Funkcia id () vracia adresu objektu v pamäti, ktorá sa pre daný objekt nemení.

__bool __ (self)

Metóda vracia True alebo False, podľa toho ako sa daný objekt vyhodnotí. Číselné objekty dávajú False pre nulové hodnoty a kontajnery (zoznam n-tica, ...) dávajú False, ak sú prázdne.

class Point:

    ...

    def __bool__(self):
        return bool(self.x and self.y)

Volanie objektu

__call __ (self, * args, ** kwargs)

Vďaka tejto metóde môže volať objekt ako by to bola funkcia. Získame tak napríklad "vylepšenú" funkciu, ktorá si môže uchovávať stavové informácie.

Napríklad faktoriál, ktorý si ukladá vypočítané hodnoty:

class Factorial:

    def __init__(self):
        self.cache = {}

    def fact(self, number):
        if number == 0:
            return 1
        else:
            return number * self.fact(number-1)

    def __call__(self, number):
        if number in self.cache:
            return self.cache[number]
        else:
            result = self.fact(number)
            self.cache[number] = result
            return result

factorial = Factorial()
print(factorial(200))
print(factorial(2))
print(factorial(200))
print(factorial.cache)

Správca kontextu

Metódy __enter __ () a __exit __ () slúži, na vytváranie vlastných správcov kontextu. Správca kontextu pred vstupom do bloku with sa zavolá metóda __enter __ () a pri výstupe sa zavolá metóda __exit __ ().

__enter __ (self)

Metóda sa zavolá pri vstupe do kontextu správcom kontextu. Ak metóda vracia nejakú hodnotu, tak sa uloží do premennej

nasledujú za výrazom as.

with smt_ctx() as value:
    do_sth() # zde provádíme příkazy

__exit __ (self, type, value, Traceback)

Táto metóda sa volá pri opúšťaní bloku with. Premenná type obsahuje výnimku, ak v bloku with nejaká nastala, ak nie, tak obsahuje None.

V diele venovanom výnimkám sa na správcu kontextu pozrieme podrobnejšie a nejaký si vytvoríme.

V budúcej lekcii, Magické metódy Pythone - Matematické , sa pozrieme na matematické funkcie a operátormi.


 

Predchádzajúci článok
Riešené úlohy k 10-11. lekciu Python
Všetky články v sekcii
Objektovo orientované programovanie v Pythone
Preskočiť článok
(neodporúčame)
Magické metódy Pythone - Matematické
Článok pre vás napísal gcx11
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
(^_^)
Aktivity