Decoratori Python: Cum să-l folosiți și de ce?

Un decorator acceptă o funcție, adaugă unele funcționalități și o returnează. În acest tutorial, veți afla cum puteți crea un decorator și de ce ar trebui să-l utilizați.

Decoratori în Python

Python are o caracteristică interesantă numită decoratori pentru a adăuga funcționalitate unui cod existent.

Aceasta se mai numește metaprogramare, deoarece o parte a programului încearcă să modifice o altă parte a programului în timpul compilării.

Condiții preliminare pentru învățarea decoratorilor

Pentru a înțelege despre decoratori, trebuie mai întâi să știm câteva lucruri de bază în Python.

Trebuie să fim confortabili cu faptul că totul în Python (Da! Chiar și cursurile) sunt obiecte. Numele pe care le definim sunt pur și simplu identificatori legați de aceste obiecte. Funcțiile nu sunt excepții, sunt și obiecte (cu atribute). Diverse nume diferite pot fi legate de același obiect funcțional.

Iată un exemplu.

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

Ieșire

 bună bună

Când rulați codul, ambele funcții firstși seconddau aceeași ieșire. Aici, numele firstși secondse referă la același obiect funcțional.

Acum lucrurile încep să devină mai ciudate.

Funcțiile pot fi transmise ca argumente către o altă funcție.

Dacă aveți funcții utilizate , cum ar fi map, filterși reduceîn Python, atunci știi deja despre acest lucru.

Astfel de funcții care iau alte funcții ca argumente sunt numite și funcții de ordin superior . Iată un exemplu al unei astfel de funcții.

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

Invocăm funcția după cum urmează.

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

În plus, o funcție poate returna o altă funcție.

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

Ieșire

 Buna ziua

Aici is_returned()este o funcție imbricată care este definită și returnată de fiecare dată când apelăm is_called().

În cele din urmă, trebuie să știm despre Închideri în Python.

Revenind la Decoratori

Funcțiile și metodele sunt numite apelabile așa cum pot fi numite.

De fapt, orice obiect care implementează __call__()metoda specială este denumit apelabil. Deci, în sensul cel mai de bază, un decorator este un apelabil care returnează un apelabil.

Practic, un decorator acceptă o funcție, adaugă unele funcționalități și o returnează.

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

Când rulați următoarele coduri în shell,

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

În exemplul prezentat mai sus, make_pretty()este un decorator. În pasul de atribuire:

 pretty = make_pretty(ordinary)

Funcția a ordinary()fost decorată și funcției returnate i s-a dat numele pretty.

Putem vedea că funcția decorator a adăugat câteva funcționalități noi funcției originale. Acest lucru este similar cu ambalarea unui cadou. Decoratorul acționează ca un înveliș. Natura obiectului care a fost decorat (cadou propriu-zis în interior) nu se modifică. Dar acum, arată frumos (de când a fost decorat).

În general, decorăm o funcție și o redistribuim ca,

 ordinary = make_pretty(ordinary).

Acesta este un construct comun și din acest motiv, Python are o sintaxă pentru a simplifica acest lucru.

Putem folosi @simbolul împreună cu numele funcției decorator și îl putem așeza deasupra definiției funcției care urmează să fie decorată. De exemplu,

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

este echivalent cu

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

Acesta este doar un zahăr sintactic pentru implementarea decoratorilor.

Funcții de decorare cu parametri

Decoratorul de mai sus a fost simplu și a funcționat doar cu funcții care nu aveau niciun parametru. Ce se întâmplă dacă am avea funcții care includ parametri precum:

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

Această funcție are doi parametri, a și b. Știm că va da o eroare dacă trecem în b ca 0.

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

Acum să facem un decorator pentru a verifica acest caz care va cauza eroarea.

 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)

Această nouă implementare va reveni Nonedacă apare condiția de eroare.

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

În acest mod, putem decora funcții care iau parametri.

Un observator dornic va observa că parametrii inner()funcției imbricate din interiorul decoratorului sunt aceiași cu parametrii funcțiilor pe care le decorează. Luând în considerare acest lucru, acum putem crea decoratori generali care funcționează cu orice număr de parametri.

În Python, această magie se face așa function(*args, **kwargs). În acest fel, argsva fi tuplul argumentelor poziționale și kwargsva fi dicționarul argumentelor cuvintelor cheie. Un exemplu de astfel de decorator va fi:

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

Decoratori în lanț în Python

Mai mulți decoratori pot fi înlănțuiți în Python.

Aceasta înseamnă că o funcție poate fi decorată de mai multe ori cu decorați diferiți (sau aceiași). Pur și simplu așezăm decoratorii deasupra funcției dorite.

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

Ieșire

 ****************************** %%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%% Buna ziua %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ********* *********************

Sintaxa de mai sus a,

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

este echivalent cu

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

Contează ordinea în care lanțăm decoratorii. Dacă am fi inversat ordinea ca,

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

Rezultatul ar fi:

 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ******************** ********** Buna ziua ****************************** %%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%

Articole interesante...