Fermer

août 2, 2022

Comprendre les décorateurs Python, avec des exemples


Cet article vous aidera à comprendre le concept de décorateurs dans la programmation Python et comment les utiliser au mieux. Nous verrons ce que sont les décorateurs Python, à quoi ressemble leur syntaxe, comment les identifier dans un script ou un framework et comment les appliquer vous-même.

UN décorateur de fonctions en Python est juste une fonction qui prend une autre fonction comme argument, étendant la fonctionnalité de la fonction décorée sans changer sa structure. Un décorateur enveloppe une autre fonction, amplifie son comportement et la renvoie.

Le concept de décorateurs en Python aide à conserver votre code SEC. Un décorateur de fonction évite les répétitions inutiles dans votre base de code, car certains morceaux de code répétés peuvent être rassemblés pour devenir des décorateurs de fonction. Au fur et à mesure que vous avancez dans l’utilisation de Python pour le développement, les décorateurs peuvent vous aider avec l’analyse et la journalisation. Ils sont également essentiels pour configurer la validation et les contrôles d’exécution.

Au fur et à mesure que nous procédons, je suppose que vous avez une compréhension de base des fonctions et de la programmation Python et que vous avez au moins Python 3.8 installé sur votre appareil.

Choses à comprendre avant de plonger dans les décorateurs Python

En Python, les fonctions sont des objets de première classe, ce qui signifie qu’elles peuvent recevoir des arguments ou être transmises en tant qu’arguments. Pour saisir pleinement le concept de décorateurs, il y a quelques choses que vous devez comprendre.

Une fonction est un objet, ce qui signifie qu’elle peut être affectée à une autre variable

def greet():
    print("Hello John")
    
greet_john = greet
greet_john()
>>>
Hello John

Rappelez-vous toujours que tout est un objet en Python. De la même manière que vous affectez des valeurs à une variable, une fonction peut également être affectée à une variable si nécessaire. Ceci est important lorsque vous apprenez à connaître les décorateurs.

Une fonction peut être renvoyée par une autre fonction

def greet():
    def greeting_at_dawn():
        print("Good morning")
        
    return greeting_at_dawn

salute = greet()
salute()
>>>
Good morning

Une fonction interne en Python peut être renvoyée à partir de la fonction externe. Cela fait partie des concepts de programmation fonctionnelle que vous rencontrerez.

Une fonction peut être passée en argument d’une autre fonction

def greet_some(func):
    print("Good morning", end=' ')
    func()

def say_name():
    print("John")

greet(say_name)
>>>
Good morning John

Une fonction qui reçoit un argument de fonction est connue sous le nom de fonction d’ordre supérieur.

Les points énumérés ci-dessus sont importants à garder à l’esprit lorsque vous apprenez à implémenter des décorateurs et à les utiliser efficacement dans un programme Python.

Comment fonctionnent les décorateurs Python

Une simple fonction de décorateur commence par une définition de fonction, la fonction de décorateur, puis une fonction imbriquée dans la fonction wrapper externe.

Gardez toujours ces deux points principaux à l’esprit lors de la définition des décorateurs :

  1. Pour implémenter des décorateurs, définissez une fonction externe qui prend un argument de fonction.
  2. Imbriquer une fonction wrapper dans la fonction décorateur externe, qui encapsule également la fonction décorée.

Voici à quoi ressemble la fonction de décorateur la plus basique dans l’extrait de code ci-dessous :

def increase_number(func):
    def increase_by_one():
        print("incrementing number by 1 ...")
        number_plus_one = func()  + 1
        return number_plus_one
    return increase_by_one 
    
def get_number():
    return 5
    
get_new_number = increase_number(get_number)
print(get_new_number())
>>>
incrementing number by 1 ...
6

En regardant le code ci-dessus, la fonction externe increase_number – également connu sous le nom de décorateur – reçoit un argument de fonction func. increase_by_one est la fonction wrapper où le décoré get_number fonction est trouvée. Le décorateur est affecté à une autre variable. Voici à quoi ressemble une syntaxe de décorateur lors de l’utilisation de décorateurs Python. Cependant, il existe un moyen beaucoup plus simple de représenter les décorateurs.

Une fonction de décorateur simple est facilement identifiée lorsqu’elle commence par le @ préfixe, couplé avec la fonction décorée en dessous. L’exemple précédent peut être refactorisé pour ressembler à ceci :

def increase_number(func):
    def increase_by_one():
        print("incrementing number by 1 ...")
        number_plus_one = func()  + 1
        return number_plus_one
    return increase_by_one 
    
@increase_number    
def get_number():
    return 5

print(get_number())
>>>
incrementing number by 1 ...
6

Les exemples montrent qu’un décorateur étend la fonctionnalité de son argument de fonction.

Fonctions de décorateur avec paramètres

Dans certains cas, vous devrez peut-être transmettre des paramètres à un décorateur. La solution consiste à transmettre des paramètres à la fonction wrapper, qui sont ensuite transmis à la fonction décorée. Voir l’exemple suivant :

 def multiply_numbers(func):
    def multiply_two_numbers(num1, num2):
        print("we're multiplying two number {} and {}".format(num1, num2))
        return func(num1, num2)
        
    return multiply_two_numbers

@multiply_numbers
def multiply_two_given_numbers(num1, num2):
    return f'{num1} * {num2} = {num1 * num2}'
    
print(multiply_two_given_numbers(3, 4))
 >>>
we're multiplying two number 3 and 4
3 * 4 = 12

Le fait de transmettre des paramètres à la fonction interne ou à la fonction imbriquée la rend encore plus puissante et robuste, car elle donne plus de flexibilité pour manipuler la fonction décorée. N’importe quel nombre d’arguments (*args) ou des arguments de mots clés (**kwargs) peut être passé à la fonction décorée. *args permet la collecte de tous les arguments de position, tandis que le **kwargs est pour tous les arguments de mots clés nécessaires lors de l’appel de la fonction. Prenons un autre exemple simple :

def decorator_func(decorated_func):
    def wrapper_func(*args, **kwargs):
        print(f'there are {len(args)} positional arguments and {len(kwargs)} keyword arguments')
        return decorated_func(*args, **kwargs)
        
    return wrapper_func

@decorator_func
def names_and_age(age1, age2, name1='Ben', name2='Harry'):
    return f'{name1} is {age1} yrs old and {name2} is {age2} yrs old'
    
print(names_and_age(12, 15, name1="Lily", name2="Ola"))
>>>
There are 2 positional arguments and 2 keyword arguments
Lily is 12 yrs old and Ola is 15 yrs old

A partir de l’exemple ci-dessus, *args forme un itérable d’arguments positionnels sous la forme d’un tuple, tandis que le **kwargs forme un dictionnaire d’arguments de mots-clés.

Décorateurs multiples ou chaînage de décorateurs en Python

Il existe plusieurs options à explorer lors de l’utilisation de décorateurs de fonctions dans votre projet Python. Un autre cas d’utilisation serait de chaîner des décorateurs (deux ou plus) à une fonction. Une fonction peut être décorée avec plus d’un décorateur (plusieurs décorateurs), et ceci est réalisé en empilant un décorateur au-dessus de l’autre sans ordre particulier. Vous obtiendrez le même résultat quel que soit l’ordre dans lequel les multiples décorateurs sont placés les uns sur les autres, comme le montre l’exemple suivant :

def increase_decorator(func):
    def increase_by_two():
        print('Increasing number by 2')
        new_number = func()
        return new_number + 2
        
    return increase_by_two
    

def decrease_decorator(func):
    def decrease_by_one():
        print('Decreasing number by 1')
        new_number = func()
        return new_number - 1
        
    return decrease_by_one
    
@increase_decorator
@decrease_decorator
def get_number():
    return 5
    
print(get_number())
>>> 
Increasing number by 2
Decreasing number by 1
6

Cas d’utilisation réels des décorateurs Python

Une façon très populaire d’utiliser les décorateurs en Python est comme un enregistreur de temps. Cela aide un programmeur à connaître le temps nécessaire à l’exécution d’une fonction pour mesurer l’efficacité.

Mémoïsation est une autre façon intéressante d’utiliser les décorateurs en Python. Les résultats des appels de fonction qui sont répétés sans aucun changement peuvent être facilement mémorisés lors de calculs ultérieurs. Vous pouvez simplement mémoriser une fonction avec des décorateurs.

Décorateurs Python intégrés comme @classmethod (méthode de classe), @staticmethod (méthode statique), et @property sont très populaires dans le modèle de décorateur OOP de Python.

Conclusion

Les décorateurs Python appliquent le principe DRY du génie logiciel car ils servent de code réutilisable. Pensez aux nombreuses fonctions Python que vous pourriez refactoriser en décorateurs. Dans cet article, nous avons exploré différentes formes de décorateurs. Il y a aussi des décorateurs de classe, bien que nous n’en ayons pas parlé ici.

Les décorateurs facilitent l’ajout de fonctionnalités supplémentaires à une simple fonction, méthode ou classe sans avoir à modifier son code source tout en gardant votre code SEC. Essayez vous-même les fonctions de décoration pour mieux comprendre le motif du décorateur.




Source link