Python dekorátorok: Hogyan kell használni és miért?

A dekoratőr felvesz egy funkciót, hozzáad néhány funkciót és visszaadja. Ebben az oktatóanyagban megtudhatja, hogyan hozhat létre dekoratort, és miért kell használni.

Dekorátorok a Pythonban

A Python egy érdekes funkcióval rendelkezik, amelyet dekorátoroknak hívnak , hogy funkcionalitást adhassanak egy meglévő kódhoz.

Ezt metaprogramozásnak is nevezik, mert a program egy része fordításkor megpróbálja módosítani a program egy másik részét.

A dekorátorok tanulásának előfeltételei

Annak érdekében, hogy megértsük a dekorátorokat, először meg kell ismernünk néhány alapvető dolgot a Pythonban.

Meg kell éreznünk magunkat azzal a ténnyel, hogy a Pythonban (Igen! Még osztályokban is) minden objektum. Az általunk definiált nevek egyszerűen azonosítók ezekhez az objektumokhoz. A függvények nem kivételek, objektumok is (attribútumokkal). Különböző nevek köthetők ugyanahhoz a függvényobjektumhoz.

Itt egy példa.

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

Kimenet

 Helló helló

Amikor futtatja a kódot, mindkét funkció firstés secondígy ugyanazt a kimenetet. Itt a nevek firstés secondugyanazon függvényobjektumra utalnak.

Most már egyre furcsábbak a dolgok.

A függvények argumentumként átadhatók egy másik függvénynek.

Ha használt funkciók, mint a map, filterés reducea Python, akkor már erről.

Az olyan függvényeket, amelyek más funkciókat argumentumként vesznek fel, magasabb rendû függvényeknek is nevezzük . Itt van egy példa egy ilyen funkcióra.

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

A függvényt az alábbiak szerint hívjuk meg.

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

Ezenkívül egy függvény visszaadhat egy másik függvényt.

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

Kimenet

 Szia

Itt is_returned()van egy beágyazott függvény, amelyet minden híváskor meghatározunk és visszaküldünk is_called().

Végül tudnunk kell a Python bezárásairól.

Visszatérés a lakberendezőkhöz

A függvényeket és módszereket hívhatónak nevezzük, ahogy hívhatók .

Valójában minden olyan objektumot, amely a speciális __call__()módszert alkalmazza, hívhatónak nevezzük. Tehát a legalapvetőbb értelemben a dekoratőr hívható, amely visszahívhatót ad vissza.

Alapvetően a dekoratőr felvesz egy funkciót, hozzáad bizonyos funkciókat és visszaadja azokat.

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

Amikor a következő kódokat futtatja héjban,

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

A fenti példában make_pretty()egy dekoratőr. A hozzárendelés lépésében:

 pretty = make_pretty(ordinary)

A függvény ordinary()díszítésre került, és a visszaküldött függvény kapta a nevet pretty.

Láthatjuk, hogy a dekorátor funkció néhány új funkcióval bővítette az eredeti funkciót. Ez hasonló az ajándék csomagolásához. A dekoratőr burkolóként működik. A díszített tárgy jellege (a tényleges ajándék belül) nem változik. De most szépnek tűnik (mivel díszítették).

Általában díszítünk egy függvényt, és hozzárendeljük,

 ordinary = make_pretty(ordinary).

Ez egy általános konstrukció, és emiatt a Python szintaxissal rendelkezik ennek egyszerűsítésére.

Használhatjuk a @szimbólumot a díszítő funkció nevével együtt, és a díszítendő függvény meghatározása fölé helyezhetjük. Például,

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

egyenértékű

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

Ez csak egy szintaktikai cukor a dekorátorok megvalósításához.

Funkciók díszítése paraméterekkel

A fenti dekoratőr egyszerű volt, és csak olyan funkciókkal működött, amelyeknek semmi paramétere nem volt. Mi lenne, ha olyan függvényeink lennének, amelyek a következő paramétereket veszik fel:

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

Ennek a funkciónak két paramétere van, az a és a b. Tudjuk, hogy hibát ad, ha b-ben 0-t adunk be.

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

Most készítsünk dekoratort, hogy ellenőrizzük ezt az esetet, amely a hibát okozza.

 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)

Ez az új megvalósítás visszatér, Noneha a hibaállapot felmerül.

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

Ily módon díszíthetjük azokat a függvényeket, amelyek paramétereket vesznek fel.

Egy figyelmes megfigyelő észreveszi, hogy inner()a dekorátoron belüli beágyazott függvény paraméterei megegyeznek az általa díszített funkciók paramétereivel. Ennek figyelembevételével most olyan általános dekorátorokat készíthetünk, amelyek tetszőleges számú paraméterrel működnek.

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 ****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

érdekes cikkek...