Decoratori în Python
Cu un decorator, poți da unei funcții un comportament suplimentar, fără să-i atingi codul.
Un decorator este el însuși o funcție. Primește o funcție ca intrare și returnează una nouă.
Decorator de bază
Mai întâi definește decoratorul, apoi scrie @decorator_name pe rândul de deasupra funcției căreia vrei să-l aplici.
În exemplu, rândul @changecase, aflat chiar deasupra lui myfunction, o „decorează” cu changecase.
Așadar, changecase este decoratorul.
Iar myfunction este funcția care este decorată.
Exemplu
Acest decorator transformă în majuscule orice returnează funcția decorată.
changecase care transformă valoarea returnată de o funcție în majuscule.
Aplică-l unei funcții care returnează "Hello Sally" și afișează rezultatul.
Ieșire: program Python complet
def changecase(func):
def myinner():
return func().upper()
return myinner
@changecase
def myfunction():
return "Hello Sally"
print(myfunction())
Apelarea repetată a unui decorator
Un decorator poate fi refolosit de câte ori vrei. Pune-l pur și simplu deasupra fiecărei funcții care are nevoie de el.
Exemplu
Același decorator @changecase aplicat pe două funcții:
changecase care transformă valorile returnate în majuscule.
Aplică-l pe două funcții diferite și afișează ambele rezultate.
Ieșire: program Python complet
def changecase(func):
def myinner():
return func().upper()
return myinner
@changecase
def myfunction():
return "Hello Sally"
@changecase
def otherfunction():
return "I am speed!"
print(myfunction())
print(otherfunction())
Argumente în funcția decorată
Și o funcție care are nevoie de argumente poate fi decorată, atâta timp cât funcția wrapper transmite mai departe acele argumente:
Exemplu
Decorarea unei funcții care primește un argument:
changecase care funcționează cu o funcție ce primește un argument.
Funcția decorată trebuie să returneze un salut scris cu majuscule.
Ieșire: program Python complet
def changecase(func):
def myinner(x):
return func(x).upper()
return myinner
@changecase
def myfunction(nam):
return "Hello " + nam
print(myfunction("John"))
*args și **kwargs
Un decorator nu poate ști întotdeauna ce argumente va primi funcția decorată. Soluția este să-i dai funcției wrapper parametrii (*args, **kwargs). Astfel, poate primi orice număr de argumente de orice tip și le poate transmite mai departe funcției decorate.
Exemplu
Funcția wrapper devine sigură cu *args și **kwargs:
changecase care folosește *args și **kwargs pentru flexibilitate.
Aplică-l unei funcții care primește un parametru pentru nume.
Ieșire: program Python complet
def changecase(func):
def myinner(*args, **kwargs):
return func(*args, **kwargs).upper()
return myinner
@changecase
def myfunction(nam):
return "Hello " + nam
print(myfunction("John"))
Decorator cu argumente
Ca să-i dai unui decorator propriile argumente, înfășoară-l într-un nivel suplimentar de funcție.
Exemplu
O fabrică de decoratori: argumentul pe care îl primește decide cu ce fel de litere va fi scris rezultatul.
changecase care primește un argument n.
Dacă n==1, transformă rezultatul în litere mici; altfel, transformă-l în majuscule.
Ieșire: program Python complet
def changecase(n):
def changecase(func):
def myinner():
if n == 1:
a = func().lower()
else:
a = func().upper()
return a
return myinner
return changecase
@changecase(1)
def myfunction():
return "Hello Linus"
print(myfunction())
Mai mulți decoratori
O funcție poate avea mai mulți decoratori.
Pune pur și simplu apelurile decoratorilor unul peste altul.
Sunt aplicați în ordine inversă, începând cu decoratorul cel mai apropiat de funcție.
Exemplu
Un decorator adaugă un salut, iar celălalt transformă textul în majuscule:
changecase (majuscule) și addgreeting (adaugă un text de salut).
Aplică-i pe amândoi, unul peste altul, unei funcții și afișează rezultatul.
Ieșire: program Python complet
def changecase(func):
def myinner():
return func().upper()
return myinner
def addgreeting(func):
def myinner():
return "Hello " + func() + " Have a good day!"
return myinner
@changecase
@addgreeting
def myfunction():
return "Tobias"
print(myfunction())
Păstrarea metadatelor funcției
Fiecare funcție Python are metadate, pe care le poți citi prin atributele ei __name__ și __doc__.
Când decorezi o funcție însă, acele metadate se pierd, iar valorile originale nu mai sunt disponibile.
Biblioteca standard are o soluție: funcția functools.wraps păstrează numele și docstring-ul funcției originale.
Exemplu
O funcție nedecorată își raportează propriul nume prin __name__:
__name__.
Ieșire: program Python complet
def myfunction():
return "Have a great day!"
print(myfunction.__name__)
Exemplu
Întreabă o funcție decorată care îi este numele și răspunsul este diferit:
__name__ original.
Afișează __name__ al unei funcții decorate.
Ieșire: program Python complet
def changecase(func):
def myinner():
return func().upper()
return myinner
@changecase
def myfunction():
return "Have a great day!"
print(myfunction.__name__)
Exemplu
Cu functools.wraps importat și aplicat, numele și docstring-ul originale se păstrează.
functools.wraps pentru a păstra numele original al funcției la decorare.
Afișează __name__ al funcției decorate.
Ieșire: program Python complet
import functools
def changecase(func):
@functools.wraps(func)
def myinner():
return func().upper()
return myinner
@changecase
def myfunction():
return "Have a great day!"
print(myfunction.__name__)