Co jsou magické metody v Pythonu a jak je používat

Jednou z méně nápadných, avšak velmi užitečných vlastností jazyka Python je možnost implementovat takzvané magické metody do objektů. Díky nim můžeme psát kód, který je srozumitelnější, intuitivnější a snadněji se s ním pracuje.

S pomocí magických metod můžeme vytvářet rozhraní pro interakci s objekty, která působí přirozeněji a více v duchu Pythonu. Tento článek vám představí magické metody, ukáže, jak je správně vytvářet, a prozkoumá běžné magické metody, se kterými se můžete setkat.

Co jsou magické metody?

Magické metody jsou speciální metody v Pythonu, které definují, jak se objekty chovají při provádění standardních operací. Tyto metody jsou jasně označené dvojitým podtržítkem na začátku i na konci názvu.

Často se jim proto říká „dunder metody“ (z anglického „double underscore“). Známým příkladem je metoda `__init__()`, která slouží k definování konstruktorů tříd.

Dunder metody se obvykle nevolají přímo v kódu. Místo toho je volá interpret Pythonu během běhu programu, když dojde k určité události.

Proč jsou magické metody užitečné?

Magické metody jsou klíčovým konceptem v objektově orientovaném programování v Pythonu. Umožňují nám definovat chování vlastních datových typů, když se s nimi provádí běžné operace. Mezi tyto operace patří:

🢢 Aritmetické výpočty

🢢 Porovnávání hodnot

🟢 Operace životního cyklu objektu

🢢 Reprezentace objektu

V následujících částech si ukážeme, jak implementovat magické metody, které definují, jak se naše aplikace chová ve všech výše uvedených případech.

Jak definovat magické metody

Jak jsme si řekli, magické metody určují chování objektů. Proto se definují jako součást třídy objektu. Protože jsou součástí třídy, mají jako první argument vždy `self`, což je odkaz na samotný objekt.

Mohou také přijímat další argumenty, v závislosti na tom, jak je interpret Pythonu volá. Jsou vždy jasně identifikovány dvěma podtržítky před a za názvem.

Implementace

Mnohé z toho, o čem jsme mluvili, se může zdát teoretické. Proto si nyní ukážeme praktický příklad. Budeme implementovat jednoduchou třídu `Rectangle` (Obdélník).

Tato třída bude mít vlastnosti pro délku a šířku. Pomocí metody `__init__` budeme moci tyto vlastnosti nastavit při vytváření instance. Dále budeme moci porovnávat různé obdélníky pomocí operátorů `==`, `<` a `>` a zjistit, zda je jeden stejný, menší nebo větší než druhý. Nakonec by měl obdélník umět poskytnout smysluplnou textovou reprezentaci.

Nastavení vývojového prostředí

Pro tento návod budete potřebovat prostředí Python. Můžete použít lokální instalaci Pythonu nebo online kompilátor Pythonu.

Vytvoření třídy Obdélník

Začneme definicí třídy `Rectangle`:

class Rectangle:
    pass

Vytvoření metody konstruktoru

Nyní vytvoříme naši první magickou metodu, konstruktor třídy. Tato metoda převezme výšku a šířku a uloží je jako atributy instance třídy.

class Rectangle:
    def __init__(self, height, width):
        self.height = height
        self.width = width

Vytvoření magické metody pro textovou reprezentaci

Dále chceme vytvořit metodu, která umožní naší třídě generovat textovou reprezentaci objektu, která je čitelná pro člověka. Tato metoda se volá vždy, když zavoláme funkci `str()` s instancí třídy `Rectangle` jako argumentem. Volá se také, když voláme funkce, které očekávají textový argument, jako je například `print()`.

class Rectangle:
    def __init__(self, height, width):
        self.height = height
        self.width = width

    def __str__(self):
        return f'Rectangle({self.height}, {self.width})'

Metoda `__str__()` by měla vrátit text, který reprezentuje objekt. V tomto případě vracíme text ve formátu `Rectangle(, <šířka>)`, kde výška a šířka jsou uložené rozměry obdélníku.

Vytváření magických metod pro porovnávání

Nyní vytvoříme operátory porovnávání pro rovnost, menší než a větší než. Tomu se říká přetížení operátoru. Použijeme magické metody `__eq__`, `__lt__` a `__gt__`. Tyto metody vrátí logickou hodnotu po porovnání obsahů obdélníků.

class Rectangle:
    def __init__(self, height, width):
        self.height = height
        self.width = width

    def __str__(self):
        return f'Rectangle({self.height}, {self.width})'

    def __eq__(self, other):
        """ Kontrola rovnosti """
        return self.height * self.width == other.height * other.width

    def __lt__(self, other):
        """ Kontrola, zda je obdélník menší než druhý """
        return self.height * self.width < other.height * other.width

    def __gt__(self, other):
        """ Kontrola, zda je obdélník větší než druhý """
        return self.height * self.width > other.height * other.width

Jak vidíte, tyto metody mají dva parametry. První je aktuální obdélník a druhý je hodnota, se kterou se porovnává. Tato hodnota může být jiná instance obdélníku nebo jakákoli jiná hodnota. Logika porovnávání a podmínky, za kterých se porovnání vyhodnotí jako pravdivé, jsou zcela na vás.

Běžné magické metody

V této části probereme běžné magické metody, se kterými se pravděpodobně setkáte.

#1. Aritmetické operace

Aritmetické magické metody se volají, když je instance vaší třídy umístěna na levé straně aritmetického znaménka. Metoda se volá se dvěma argumenty, první je odkaz na instanci a druhý je objekt napravo od znaménka. Metody a znaménka jsou následující:

Název Metoda Znaménko Popis
Sčítání `__add__` `+` Implementuje sčítání.
Odčítání `__sub__` `–` Implementuje odčítání.
Násobení `__mul__` `*` Implementuje násobení.
Dělení `__div__` `/` Implementuje dělení.
Celočíselné dělení `__floordiv__` `//` Implementuje celočíselné dělení.

#2. Porovnávací operace

Podobně jako aritmetické magické metody se i tyto metody volají, když je instance třídy, pro kterou jsou definovány, umístěna nalevo od operátoru porovnávání. Stejně jako u aritmetických magických metod se i zde volají dvěma parametry, první je odkaz na instanci objektu a druhý je odkaz na hodnotu na pravé straně znaménka.

Název Metoda Znaménko Popis
Menší než `__lt__` `<` Implementuje porovnání menší než.
Větší než `__gt__` `>` Implementuje porovnání větší než.
Rovno `__eq__` `==` Implementuje porovnání rovnosti.
Menší nebo rovno `__le__` `<=` Implementuje porovnání menší nebo rovno.
Větší nebo rovno `__ge__` `>=` Implementuje porovnání větší nebo rovno.

#3. Operace životního cyklu

Tyto metody se volají v reakci na různé události životního cyklu objektu, jako je vytvoření instance nebo její odstranění. Konstruktor `__init__` patří do této kategorie. Běžné metody v této kategorii jsou uvedeny v tabulce níže:

Název Metoda Popis
Konstruktor `__init__` Tato metoda se volá vždy, když je vytvořena instance třídy.
Destruktor `__del__` Tato metoda se volá vždy, když je instance třídy smazána. Lze ji použít pro úklid, například pro zavření otevřených souborů.
Vytvoření `__new__` Metoda `__new__` se volá jako první při vytváření instance objektu. Volá se před konstruktorem a přijímá třídu i všechny další argumenty. Vrací instanci třídy. Není příliš často využívaná, ale je dobré o ní vědět.

#4. Reprezentace

Název Metoda Popis
Textová reprezentace `__str__` Vrací textovou reprezentaci objektu, která je čitelná pro člověka. Tato metoda se volá, když zavoláme funkci `str()` a předáme instanci třídy jako argument. Volá se také, když předáme instanci funkcím `print()` a `format()`. Jejím účelem je poskytnout řetězec, který je srozumitelný pro koncového uživatele aplikace.
Interní reprezentace `__repr__` Vrací textovou reprezentaci objektu, která je určena pro vývojáře. Ideálně by vracený řetězec měl obsahovat všechny informace, které jsou potřebné k vytvoření identické instance objektu.

Doporučení pro tvorbu magických metod

Magické metody jsou mocný nástroj, který může zjednodušit váš kód. Nicméně je důležité mít na paměti následující doporučení:

  • Používejte je s rozvahou – příliš mnoho magických metod ve třídách může ztížit porozumění kódu. Implementujte jen ty, které jsou skutečně nezbytné.
  • Ujistěte se, že rozumíte dopadům metod jako `__setattr__` a `__getattr__` na výkon. Používejte je s rozmyslem.
  • Dokumentujte chování vašich magických metod, aby ostatní vývojáři přesně věděli, co dělají. To usnadní jejich použití a ladění.

Závěrem

V tomto článku jsme si představili magické metody jako způsob, jak vytvářet třídy, které lze používat se standardními operacemi. Ukázali jsme si, jak se definují, a probrali jsme příklad třídy, která magické metody implementuje. Dále jsme probrali různé metody, se kterými se pravděpodobně setkáte, a na závěr jsme se podělili o několik doporučení, která je dobré mít na paměti.

Možná by vás také mohlo zajímat, jak implementovat třídu `Counter` v Pythonu.