11. díl - Magické metody v Pythonu

Python Objektově orientované programování Magické metody v Pythonu

V tomto dílu tutoriálu o programovacím jazyku Python se podíváme na magické metody objektů.

Magické metody

Magické metody objektů jsou takové metody, které začínájí a končí dvěmi podtržítky. S několika těmito metodami jsme se už setkali, jako například s magickou metodou __init__ pro inicializaci objektu nebo s metodou __str__ pro vypsání jeho lidsky čitelné reprezentace.

Vytváření objektů

__new__(cls, *args, **kwargs)

Metodu __new__ voláme, když potřebujeme kontrolu nad vytvářením objektu. Zejména pokud máme vlastní třídu, která dědí od vestavěných tříd jako například int (číslo) nebo str (řetězec). Někdy je lepší pro danou situaci použít deskriptory nebo návrhový vzor Factory.

Metoda __new__ vrací buď vytvořený objekt nebo nic. Pokud objekt vrátí, tak se zavolá metoda init, pokud ne, tak se metoda __init__ nevolá.

Ukázka
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)

Metoda __new__ bere jako první paramatert třídu daného objektu a poté další argumenty předané v konstruktoru. Třídy jako parametr se do metody __new__ přidává automaticky. Pokud tvorba objektu proběhne úspěšně, tak se i metoda __init__ volá s parametry z konstruktoru.

V metodě __new__ můžeme dokonce už přiřazovat atributy k objektu.

Ukázka
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 nevracíme ihned, ale uložíme ho do proměnné a přiradíme mu atributy.

__init__(self, *args, **kwargs)

Metoda __init__ se volá při inicializaci objektů. Jako první parametr bere objekt (self), který je předán automaticky. Metoda __init__ by měla vracet pouze None, který Python sám vrací, pokud metoda nemá specifikovaný návratový typ. Pokud metoda __init__ vrací něco jiného než None, tak se vyvolá TypeError.

Ukázka v interaktivní konzoli
>>> class Test:
...     def __self__(self): return 1
...
>>> test = Test()
Traceback (most recent call last):
        ...
TypeError: __init__() should return None, not 'int'

__del__(self)

Metoda __del__ se volá destuktor objektu a volá se při zničení objektu, ovšem její chování záleží na konkrétní implemetaci Pythonu. V CPythonu se volá, pokud počet referencí na objekt klesne na nulu. Příkaz del nezpůsobí přímé zavolání metody __del__, pouze sníží počet referencí o jednu. Navíc není žádná záruka, že se metoda __del__ zavolá při ukončení programy. Pro uvolňování zdrojů je lepší použít blok try-finally nebo správce kontextu.

Reprezentace objektů

__repr__(self)

Metoda by měla vracet textovou reprezentaci objektu tak, aby platilo:

x = eval(repr(x))

__str__(self)

Tato magická metoda by měla vracet lidsky čitelnou reprezentaci objektu a měla by vracet řetězec stejně jako metoda __repr__().

Napří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)

Tato metoda by měla vrátit reprezentaci objektu za pomoci bytů, což znamená, že by měla vracet objekt typu bytes.

__format__(self, format_spec)

Metoda slouží k formátování textové reprezentace objektu. Je volána metodou řetězce format ( str.format() )

K formátování lze použít vestavěnou funkci format, která je syntaktický cukr pro:

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

Více o formátování řetězců: https://www.python.org/…ps/pep-3101/

Ukázka metody:

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))

Pokud se metoda nepřepíše, tak použití metody format vyvolá TypeError (od CPythonu 3.4)

Porovnávací metody

Python dosazuje do porovnávacích metod odkazy na porovnávané objekty.

__lt__(self, other)

Menší než (lower than)

x < y

__le__(self, other)

Menší nebo roven (lower or equal)

x <= y

__eq__(self, other)

Rovná se (equal)

x == y

__ne__(self, other)

Nerovná se (not equal)

x != y

__gt__(self, other)

Větší než (greater than)

x > y

__ge__(self, other)

Větší nebo roven (greater or equal)

x >= y

Všechny porovnávací metody vrací True nebo False, popř. můžou vyvolat výjimku , pokud se porovnávání s druhým objektem nepodporuje.

Pří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__))

    ...

Speciální metoda __class__ uchovává odkaz na třídu objektu. V ukázce jsou uvedeny jen implementace metod __lt__ a __le__, zbytek je podobný. Dva objekty typu Point porovnáme podle vzdálenosti od počátku souřadnic (tj. bod [0, 0]).

Modul functools nabízí automatické vygenerování ostatních metod za pomoci jedné z metod __lt__(), __lg__(), __gt__() nebo __ge__() a třída by měla mít i metodu __eq__(). Avšak použití dekorátoru total_ordering může mít negativní účinek na rychlost programu.

Vzhledem k tomu, že atribut __class__ obsahuje odakz na třídu, tak za pomoci tohoto atributu lze objekt "naklonovat"

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()

Další metody

__hash__(self)

Metoda hash by měla být reimplementována, pokud je definována metoda __eq__(), aby bylo možné použít objekt v některých kolecích.

Její implemetace:

class Point:

    ...

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

Funkce id() vrací adresu objektu v paměti, která se pro daný objekt nemění.

__bool__(self)

Metoda vrací True nebo False, podle toho jak se daný objekt vyhodnotí. Číselné objekty dávají False pro nulové hodnoty a kontejnery (seznam, n-tice, ...) dávají False, pokud jsou prázdné.

class Point:

    ...

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

Volání objektu

__call__(self, *args, **kwargs)

Díky této metodě může volat objekt jako by to byla funkce. Získáme tak například "vylepšenou" fuknci, která si může uchovávat stavové informace.

Například faktoriál, který si ukládá 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ávce kontextu

Metody __enter__() a __exit__() slouží, k vytváření vlastních správců kontextu. Správce kontextu před vstupem do bloku with se zavolá metoda __enter__() a při výstupu se zavolá metoda __exit__(). Syntaxe metod je následující:

__enter__(self)

Metoda se zavolá při vstupu do kontextu správcem kontextu. Pokud metoda vrací nějakou hodnotu, tak se uloží do proměnné

následují za výrazem as.

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

__exit__(self, type, value, traceback)

Tato metoda se volá při opouštění bloku with. Proměnná type obsahuje výjimku, pokud v bloku with nějaká nastala, pokud ne, tak obsahuje None.

V díle věnovaném výjimkám se na správce kontextu podíváme podrobněji a nějaký si vytvoříme.


 

Stáhnout

Staženo 15x (3.08 kB)
Aplikace je včetně zdrojových kódů v jazyce python

 

  Aktivity (1)

Článek pro vás napsal gcx11
Avatar
(^_^)

Jak se ti líbí článek?
Celkem (4 hlasů) :
55555


 


Miniatura
Předchozí článek
Vlastnosti v Pythonu
Miniatura
Následující článek
Magické metody Pythonu - Matematické

 

 

Komentáře

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.

Zatím nikdo nevložil komentář - buď první!