Maison > développement back-end > Tutoriel Python > Comprendre les décorateurs Python en un seul article

Comprendre les décorateurs Python en un seul article

WBOY
Libérer: 2023-04-12 21:40:13
avant
1073 Les gens l'ont consulté

Comprendre les décorateurs Python en un seul article

Python est un langage adapté aux débutants. Cependant, il possède également de nombreuses fonctionnalités avancées et difficiles à maîtriser, comme les décorateurs. De nombreux débutants n'ont jamais compris les décorateurs et leur fonctionnement. Dans cet article, nous présenterons les tenants et les aboutissants des décorateurs.

En Python, une fonction est une structure très flexible. On peut l'attribuer à une variable, la passer en paramètre à une autre fonction, ou l'utiliser comme sortie d'une fonction. Un décorateur est essentiellement une fonction qui permet à d’autres fonctions d’ajouter des fonctionnalités sans modification.

C'est le sens de "décoration". Cette "décoration" représente elle-même une fonction. Si vous l'utilisez pour modifier différentes fonctions, cela signifie ajouter cette fonction à ces fonctions.

De manière générale, on peut utiliser le @sucre syntaxique (Syntactic Sugar) fourni par le décorateur pour décorer d'autres fonctions ou objets. Comme indiqué ci-dessous, nous utilisons le décorateur @dec pour décorer la fonction func () :

@dec
def func():
 pass
Copier après la connexion

La meilleure façon de comprendre le décorateur est de comprendre quel problème le décorateur résout. Cet article présentera le décorateur étape par étape en partant du spécifique. problème et montrer son élégance et sa puissance.

Problèmes de configuration

Pour comprendre le but des décorateurs, regardons un exemple simple. Supposons que vous ayez une simple fonction d'addition dec.py avec une valeur par défaut de 10 pour le deuxième paramètre :

# dec.py
def add(x, y=10):
 return x + y
Copier après la connexion

Examinons de plus près cette fonction d'addition :

>>> add(10, 20)
30
>>> add
<function add at 0x7fce0da2fe18>
>>> add.__name__
'add'
>>> add.__module__
'__main__'
>>> add.__defaults__ # default value of the `add` function
(10,)
>>> add.__code__.co_varnames # the variable names of the `add` function
('x', 'y')
Copier après la connexion

Nous n'avons pas besoin de comprendre de quoi il s'agit, rappelez-vous simplement Chaque fonction en Python est un objet et possède diverses propriétés et méthodes. Vous pouvez également afficher le code source de la fonction add() via le module inspect :

>>> from inspect import getsource
>>> print(getsource(add))
def add(x, y=10):
 return x + y
Copier après la connexion

Maintenant, vous utilisez la fonction d'addition d'une manière ou d'une autre, par exemple, vous utilisez certaines opérations pour tester la fonction :

# dec.py
from time import time
def add(x, y=10):
 return x + y
print('add(10)', add(10))
print('add(20, 30)', add(20, 30))
print('add("a", "b")', add("a", "b"))
Output: i
add(10) 20
add(20, 30) 50
add("a", "b") ab
Copier après la connexion

Si vous souhaitez comprendre la fonction de chaque opération Pour le temps, vous pouvez appeler le module time :

# dec.py
from time import time
def add(x, y=10):
 return x + y
before = time()
print('add(10)', add(10))
after = time()
print('time taken: ', after - before)
before = time()
print('add(20, 30)', add(20, 30))
after = time()
print('time taken: ', after - before)
before = time()
print('add("a", "b")', add("a", "b"))
after = time()
print('time taken: ', after - before)
Output:
add(10) 20
time taken:6.699562072753906e-05
add(20, 30) 50
time taken:6.9141387939453125e-06
add("a", "b") ab
time taken:6.9141387939453125e-06
Copier après la connexion

Maintenant, en tant que programmeur, cela vous démange un peu Après tout, nous n'aimons pas copier et coller le même code tout le temps. Le code actuel n'est pas très lisible. Si vous souhaitez changer quelque chose, vous devez tout modifier là où il apparaît. Il doit y avoir une meilleure méthode en Python.

Nous pouvons capturer le temps d'exécution directement dans la fonction add comme suit :

# dec.py
from time import time
def add(x, y=10):
 before = time()
 rv = x + y
 after = time()
 print('time taken: ', after - before)
 return rv
print('add(10)', add(10))
print('add(20, 30)', add(20, 30))
print('add("a", "b")', add("a", "b"))
Copier après la connexion

Cette méthode est nettement meilleure que la précédente. Mais si vous avez une autre fonction, cela ne semble pas pratique. Lorsque nous avons plusieurs fonctions :

# dec.py
from time import time
def add(x, y=10):
 before = time()
 rv = x + y
 after = time()
 print('time taken: ', after - before)
 return rv
def sub(x, y=10):
 return x - y
print('add(10)', add(10))
print('add(20, 30)', add(20, 30))
print('add("a", "b")', add("a", "b"))
print('sub(10)', sub(10))
print('sub(20, 30)', sub(20, 30))
Copier après la connexion

Parce que add et sub sont tous deux des fonctions, nous pouvons en profiter pour écrire une fonction timer. Nous voulons que la minuterie calcule le temps de fonctionnement d'une fonction :

def timer(func, x, y=10):
 before = time()
 rv = func(x, y)
 after = time()
 print('time taken: ', after - before)
 return rv
Copier après la connexion

C'est bien, mais nous devons envelopper différentes fonctions avec des fonctions de minuterie, comme ceci :

print('add(10)', timer(add,10)))
Copier après la connexion

La valeur par défaut est-elle toujours 10 maintenant ? pas nécessairement. Alors, comment faire mieux ?

Voici une idée : créez une nouvelle fonction de minuterie, encapsulez d'autres fonctions et renvoyez la fonction encapsulée :

def timer(func):
 def f(x, y=10):
 before = time()
 rv = func(x, y)
 after = time()
 print('time taken: ', after - before)
 return rv
 return f
Copier après la connexion

Maintenant, vous encapsulez simplement les fonctions d'ajout et de sous-fonction avec timer :

add = timer(add)
Copier après la connexion

C'est tout ! Voici le code complet :

# dec.py
from time import time
def timer(func):
 def f(x, y=10):
 before = time()
 rv = func(x, y)
 after = time()
 print('time taken: ', after - before)
 return rv
 return f
def add(x, y=10):
 return x + y
add = timer(add)
def sub(x, y=10):
 return x - y
sub = timer(sub)
print('add(10)', add(10))
print('add(20, 30)', add(20, 30))
print('add("a", "b")', add("a", "b"))
print('sub(10)', sub(10))
print('sub(20, 30)', sub(20, 30))
Output:
time taken:0.0
add(10) 20
time taken:9.5367431640625e-07
add(20, 30) 50
time taken:0.0
add("a", "b") ab
time taken:9.5367431640625e-07
sub(10) 0
time taken:9.5367431640625e-07
sub(20, 30) -10
Copier après la connexion

Résumons le processus : nous avons une fonction (comme la fonction add) puis enveloppons cette fonction avec une action (comme le timing). Le résultat du packaging est une nouvelle fonction qui peut implémenter certaines nouvelles fonctions.

Bien sûr, il y a un problème avec les valeurs par défaut, nous le corrigerons plus tard.

Décorateur

Maintenant, la solution ci-dessus est très proche de l'idée d'un décorateur. Elle utilise des comportements courants pour envelopper une fonction spécifique. Le code après avoir utilisé le décorateur est :

def add(x, y=10):
 return x + y
add = timer(add)
You write:
@timer
def add(x, y=10):
 return x + y
Copier après la connexion

Ils ont le même effet, c'est ce que font les décorateurs Python. La fonction qu'il implémente est similaire à add = timer(add), sauf que le décorateur met la syntaxe au-dessus de la fonction, et la syntaxe est plus simple : @timer.

# dec.py
from time import time
def timer(func):
 def f(x, y=10):
 before = time()
 rv = func(x, y)
 after = time()
 print('time taken: ', after - before)
 return rv
 return f
@timer
def add(x, y=10):
 return x + y
@timer
def sub(x, y=10):
 return x - y
print('add(10)', add(10))
print('add(20, 30)', add(20, 30))
print('add("a", "b")', add("a", "b"))
print('sub(10)', sub(10))
print('sub(20, 30)', sub(20, 30))
Copier après la connexion

Paramètres et paramètres de mots clés

Maintenant, il reste encore un petit problème qui n'a pas été résolu. Dans la fonction timer, nous codons en dur les paramètres x et y, c'est-à-dire que la valeur par défaut de y est 10. Il existe un moyen de transmettre des arguments et des arguments de mots-clés à la fonction, à savoir *args et **kwargs. Les paramètres sont les paramètres standard de la fonction (dans ce cas, x est le paramètre) et les paramètres mot-clé sont des paramètres qui ont déjà une valeur par défaut (dans ce cas, y=10). Le code est le suivant :

# dec.py
from time import time
def timer(func):
 def f(*args, **kwargs):
 before = time()
 rv = func(*args, **kwargs)
 after = time()
 print('time taken: ', after - before)
 return rv
 return f
@timer
def add(x, y=10):
 return x + y
@timer
def sub(x, y=10):
 return x - y
print('add(10)', add(10))
print('add(20, 30)', add(20, 30))
print('add("a", "b")', add("a", "b"))
print('sub(10)', sub(10))
print('sub(20, 30)', sub(20, 30))
Copier après la connexion

Maintenant, la fonction timer peut gérer n'importe quelle fonction, n'importe quel paramètre et n'importe quel réglage de valeur par défaut, car elle transmet simplement ces paramètres dans la fonction.

Décorateurs d'ordre supérieur

Vous vous demandez peut-être : si nous pouvons envelopper une fonction avec une autre fonction pour ajouter un comportement utile, pouvons-nous aller plus loin ? Devons-nous envelopper une fonction avec une autre fonction et être enveloppés par une autre fonction ?

Oui ! En fait, la fonction peut être aussi profonde que vous le souhaitez. Par exemple, vous souhaitez écrire un décorateur qui exécute une fonction n fois. Comme indiqué ci-dessous :

def ntimes(n):
 def inner(f):
 def wrapper(*args, **kwargs):
 for _ in range(n):
 rv = f(*args, **kwargs)
 return rv
 return wrapper
 return inner
Copier après la connexion

Ensuite, vous pouvez utiliser la fonction ci-dessus pour envelopper une autre fonction, telle que la fonction add de l'article précédent :

@ntimes(3)
def add(x, y):
 print(x + y)
 return x + y
Copier après la connexion

L'instruction de sortie montre que le code est effectivement exécuté 3 fois.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:51cto.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal