Python Decorators: How to use and why?

Dekorátor převezme funkci, přidá některé funkce a vrátí ji. V tomto tutoriálu se dozvíte, jak můžete vytvořit dekorátor a proč byste jej měli používat.

Dekoratéři v Pythonu

Python má zajímavou funkci zvanou dekoratéři, která přidává funkce do existujícího kódu.

Toto se také nazývá metaprogramování, protože část programu se pokouší upravit jinou část programu v době kompilace.

Předpoklady pro učení dekoratérů

Abychom porozuměli dekoratérům, musíme nejprve znát několik základních věcí v Pythonu.

Musíme být spokojeni s tím, že všechno v Pythonu (Ano! Dokonce i třídy) jsou objekty. Názvy, které definujeme, jsou jednoduše identifikátory vázané na tyto objekty. Funkce nejsou výjimkou, jsou to také objekty (s atributy). Ke stejnému funkčnímu objektu lze vázat různé různé názvy.

Zde je příklad.

 def first(msg): print(msg) first("Hello") second = first second("Hello")

Výstup

 Ahoj ahoj

Když spustíte kód, obě funkce firsta secondposkytují stejný výstup. Zde názvy firsta secondodkazují na stejný funkční objekt.

Nyní věci začínají být divnější.

Funkce lze předat jako argumenty jiné funkci.

Pokud jste použili funkce, jako je map, filtera reducev Pythonu, pak již víte o tom.

Takovým funkcím, které jako argumenty berou jiné funkce, se také říká funkce vyššího řádu . Zde je příklad takové funkce.

 def inc(x): return x + 1 def dec(x): return x - 1 def operate(func, x): result = func(x) return result

Funkci vyvoláme následujícím způsobem.

 >>> operate(inc,3) 4 >>> operate(dec,3) 2

Funkce navíc může vrátit jinou funkci.

 def is_called(): def is_returned(): print("Hello") return is_returned new = is_called() # Outputs "Hello" new()

Výstup

 Ahoj

Zde is_returned()je vnořená funkce, která je definována a vrácena pokaždé, když zavoláme is_called().

Nakonec musíme vědět o Closures v Pythonu.

Vracíme se k dekoratérům

Funkce a metody se nazývají volatelné, jak je lze volat.

Ve skutečnosti je každý objekt, který implementuje speciální __call__()metodu, nazýván volatelný. V nejzákladnějším smyslu je tedy dekorátor volatelný, který vrací volatelný.

V zásadě dekoratér převezme funkci, přidá některé funkce a vrátí ji.

 def make_pretty(func): def inner(): print("I got decorated") func() return inner def ordinary(): print("I am ordinary")

Když spustíte následující kódy ve skořápce,

 >>> ordinary() I am ordinary >>> # let's decorate this ordinary function >>> pretty = make_pretty(ordinary) >>> pretty() I got decorated I am ordinary

Ve výše uvedeném příkladu make_pretty()je dekoratér. V kroku přiřazení:

 pretty = make_pretty(ordinary)

Funkce ordinary()byla vyzdobena a vrácená funkce dostala jméno pretty.

Vidíme, že funkce dekorátoru přidala k původní funkci některé nové funkce. Je to podobné jako při balení dárku. Dekorátor funguje jako obal. Povaha zdobeného předmětu (skutečný dar uvnitř) se nemění. Ale teď to vypadá hezky (protože to bylo zdobené).

Obecně funkci zdobíme a přiřadíme ji jako,

 ordinary = make_pretty(ordinary).

Toto je běžný konstrukt az tohoto důvodu má Python syntaxi, která to zjednoduší.

Můžeme použít @symbol spolu s názvem funkce dekorátoru a umístit jej nad definici zdobené funkce. Například,

 @make_pretty def ordinary(): print("I am ordinary")

je ekvivalentní k

 def ordinary(): print("I am ordinary") ordinary = make_pretty(ordinary)

Jedná se pouze o syntaktický cukr pro implementaci dekoratérů.

Zdobení funkcí pomocí parametrů

Výše uvedený dekorátor byl jednoduchý a pracoval pouze s funkcemi, které neměly žádné parametry. Co kdybychom měli funkce, které přijímaly parametry jako:

 def divide(a, b): return a/b

Tato funkce má dva parametry, a a b. Víme, že udělá chybu, když předáme b jako 0.

 >>> divide(2,5) 0.4 >>> divide(2,0) Traceback (most recent call last):… ZeroDivisionError: division by zero

Nyní vytvořme dekoratér, který zkontroluje tento případ, který způsobí chybu.

 def smart_divide(func): def inner(a, b): print("I am going to divide", a, "and", b) if b == 0: print("Whoops! cannot divide") return return func(a, b) return inner @smart_divide def divide(a, b): print(a/b)

Tato nová implementace se vrátí, Nonepokud nastane chybový stav.

 >>> divide(2,5) I am going to divide 2 and 5 0.4 >>> divide(2,0) I am going to divide 2 and 0 Whoops! cannot divide

Tímto způsobem můžeme zdobit funkce, které berou parametry.

Nadšený pozorovatel si všimne, že parametry vnořené inner()funkce uvnitř dekorátoru jsou stejné jako parametry funkcí, které zdobí. Když to vezmeme v úvahu, nyní můžeme vytvořit obecné dekoratéry, které pracují s libovolným počtem parametrů.

In Python, this magic is done as function(*args, **kwargs). In this way, args will be the tuple of positional arguments and kwargs will be the dictionary of keyword arguments. An example of such a decorator will be:

 def works_for_all(func): def inner(*args, **kwargs): print("I can decorate any function") return func(*args, **kwargs) return inner

Chaining Decorators in Python

Multiple decorators can be chained in Python.

This is to say, a function can be decorated multiple times with different (or same) decorators. We simply place the decorators above the desired function.

 def star(func): def inner(*args, **kwargs): print("*" * 30) func(*args, **kwargs) print("*" * 30) return inner def percent(func): def inner(*args, **kwargs): print("%" * 30) func(*args, **kwargs) print("%" * 30) return inner @star @percent def printer(msg): print(msg) printer("Hello")

Output

 ****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Hello %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ******************************

The above syntax of,

 @star @percent def printer(msg): print(msg)

is equivalent to

 def printer(msg): print(msg) printer = star(percent(printer))

The order in which we chain decorators matter. If we had reversed the order as,

 @percent @star def printer(msg): print(msg)

The output would be:

 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ****************************** Hello ****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Zajímavé články...