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 second
dau aceeași ieșire. Aici, numele first
și second
se 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 None
dacă 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, args
va fi tuplul argumentelor poziționale și kwargs
va 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 ****************************** %%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%