Décorateurs Python
Décorateurs Python
Un décorateur prend une fonction, ajoute des fonctionnalités et la renvoie. Dans ce didacticiel, vous apprendrez comment créer un décorateur et pourquoi vous devriez l'utiliser.
Vidéo :@Decorators en Python
Décorateurs en Python
Python a une fonctionnalité intéressante appelée décorateurs pour ajouter des fonctionnalités à un code existant.
C'est aussi appelé métaprogrammation car une partie du programme essaie de modifier une autre partie du programme au moment de la compilation.
Prérequis pour apprendre les décorateurs
Afin de comprendre les décorateurs, nous devons d'abord connaître quelques éléments de base en Python.
Nous devons être à l'aise avec le fait que tout en Python (Oui ! Même les classes), sont des objets. Les noms que nous définissons sont simplement des identifiants liés à ces objets. Les fonctions ne font pas exception, ce sont aussi des objets (avec des attributs). Plusieurs noms différents peuvent être liés au même objet fonction.
Voici un exemple.
def first(msg):
print(msg)
first("Hello")
second = first
second("Hello")
Sortie
Hello Hello
Lorsque vous exécutez le code, les deux fonctions first
et second
donner la même sortie. Ici, les noms first
et second
se référer au même objet fonction.
Maintenant, les choses commencent à devenir plus étranges.
Les fonctions peuvent être passées en arguments à une autre fonction.
Si vous avez utilisé des fonctions comme map
, filter
et reduce
en Python, alors vous le savez déjà.
De telles fonctions qui prennent d'autres fonctions comme arguments sont également appelées fonctions d'ordre supérieur . Voici un exemple d'une telle fonction.
def inc(x):
return x + 1
def dec(x):
return x - 1
def operate(func, x):
result = func(x)
return result
Nous invoquons la fonction comme suit.
>>> operate(inc,3)
4
>>> operate(dec,3)
2
De plus, une fonction peut retourner une autre fonction.
def is_called():
def is_returned():
print("Hello")
return is_returned
new = is_called()
# Outputs "Hello"
new()
Sortie
Hello
Ici, is_returned()
est une fonction imbriquée qui est définie et renvoyée chaque fois que nous appelons is_called()
.
Enfin, nous devons connaître les fermetures en Python.
Revenir aux décorateurs
Les fonctions et les méthodes sont dites appelables comme on peut les appeler.
En fait, tout objet qui implémente le spécial __call__()
méthode est appelée appelable. Ainsi, dans le sens le plus élémentaire, un décorateur est un callable qui renvoie un callable.
Fondamentalement, un décorateur prend une fonction, ajoute des fonctionnalités et la renvoie.
def make_pretty(func):
def inner():
print("I got decorated")
func()
return inner
def ordinary():
print("I am ordinary")
Lorsque vous exécutez les codes suivants dans le shell,
>>> ordinary()
I am ordinary
>>> # let's decorate this ordinary function
>>> pretty = make_pretty(ordinary)
>>> pretty()
I got decorated
I am ordinary
Dans l'exemple ci-dessus, make_pretty()
est décorateur. A l'étape d'attribution :
pretty = make_pretty(ordinary)
La fonction ordinary()
a été décoré et la fonction renvoyée a reçu le nom pretty
.
Nous pouvons voir que la fonction de décorateur a ajouté de nouvelles fonctionnalités à la fonction d'origine. Ceci est similaire à l'emballage d'un cadeau. Le décorateur agit comme un wrapper. La nature de l'objet qui a été décoré (cadeau réel à l'intérieur) ne change pas. Mais maintenant, c'est joli (depuis qu'il a été décoré).
Généralement, nous décorons une fonction et la réattribuons comme,
ordinary = make_pretty(ordinary).
Il s'agit d'une construction courante et pour cette raison, Python a une syntaxe pour simplifier cela.
Nous pouvons utiliser le @
symbole avec le nom de la fonction décorateur et placez-le au-dessus de la définition de la fonction à décorer. Par exemple,
@make_pretty
def ordinary():
print("I am ordinary")
est équivalent à
def ordinary():
print("I am ordinary")
ordinary = make_pretty(ordinary)
C'est juste un sucre syntaxique pour implémenter des décorateurs.
Décorer des fonctions avec des paramètres
Le décorateur ci-dessus était simple et ne fonctionnait qu'avec des fonctions qui n'avaient aucun paramètre. Et si nous avions des fonctions qui acceptaient des paramètres comme :
def divide(a, b):
return a/b
Cette fonction a deux paramètres, a et b . Nous savons que cela donnera une erreur si nous passons en b comme 0.
>>> divide(2,5)
0.4
>>> divide(2,0)
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
Créons maintenant un décorateur pour vérifier ce cas qui causera l'erreur.
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)
Cette nouvelle implémentation renverra None
si la condition d'erreur se produit.
>>> 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
De cette manière, nous pouvons décorer des fonctions qui prennent des paramètres.
Un observateur attentif remarquera que les paramètres du inner()
imbriqué fonction à l'intérieur du décorateur est le même que les paramètres des fonctions qu'il décore. En tenant compte de cela, nous pouvons désormais créer des décorateurs généraux qui fonctionnent avec n'importe quel nombre de paramètres.
En Python, cette magie se fait sous la forme function(*args, **kwargs)
. De cette façon, args
sera le tuple des arguments positionnels et kwargs
sera le dictionnaire des arguments de mots-clés. Un exemple d'un tel décorateur sera :
def works_for_all(func):
def inner(*args, **kwargs):
print("I can decorate any function")
return func(*args, **kwargs)
return inner
Chaînage des décorateurs en Python
Plusieurs décorateurs peuvent être chaînés en Python.
C'est-à-dire qu'une fonction peut être décorée plusieurs fois avec des décorateurs différents (ou identiques). Nous plaçons simplement les décorateurs au-dessus de la fonction souhaitée.
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")
Sortie
****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Hello %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ******************************
La syntaxe ci-dessus de,
@star
@percent
def printer(msg):
print(msg)
est équivalent à
def printer(msg):
print(msg)
printer = star(percent(printer))
L'ordre dans lequel nous enchaînons les décorateurs compte. Si nous avions inversé la commande comme,
@percent
@star
def printer(msg):
print(msg)
Le résultat serait :
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ****************************** Hello ****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Python
- Types de données Python
- Opérateurs Python
- Instruction de passe Python
- Arguments de la fonction Python
- Fonction Python Anonyme/Lambda
- Fonctions Python Lambda avec EXEMPLES
- Fonction Python abs() :Exemples de valeurs absolues
- Fonction Python round() avec EXEMPLES
- Python range() Fonction :Float, List, For loop Exemples