Skip to content

Une brève explication des side effects en Python

Updated on

Dans le monde de la programmation Python, comprendre et gérer correctement les side effects est essentiel. Ces aspects souvent négligés, mais critiques, peuvent faire la différence entre un programme bien fonctionnel et un programme imprévisible et difficile à maintenir.

Les side effects en Python se produisent lorsqu'une fonction, au lieu de simplement renvoyer une valeur, modifie également un état ou a une interaction observable avec le monde extérieur. Bien qu'ils ne soient pas toujours négatifs, ces side effects peuvent introduire des bugs difficiles à retracer et à résoudre. Par conséquent, apprendre à les contrôler ou à les éviter peut améliorer considérablement la stabilité et la fiabilité de votre code.

Vous voulez créer rapidement des visualisations de données à partir d'un DataFrame Pandas Python sans code ?

PyGWalker est une bibliothèque Python pour l'analyse exploratoire de données avec visualisation. PyGWalker (opens in a new tab) peut simplifier votre flux de travail d'analyse et de visualisation de données dans Jupyter Notebook, en transformant votre DataFrame Pandas (et DataFrame Polars) en une interface utilisateur de type Tableau pour l'exploration visuelle.

PyGWalker pour la visualisation des données (opens in a new tab)

Qu'est-ce qu'un side effect en Python ?

En Python, un side effect est tout changement qu'une fonction apporte à son état ou à l'état du programme global, en plus de sa valeur de retour. Les side effects peuvent inclure la modification d'une variable globale ou statique, la modification de l'objet d'origine, la production de sortie console, ou l'écriture dans un fichier ou une base de données. Ils sont couramment utilisés dans des opérations telles que les opérations d'E/S, les mises à jour des états globaux, ou même à l'intérieur de méthodes de classe qui modifient l'état de l'objet.

# Exemple de side effect en Python
def ajouter_element(data, element):
    data.append(element)
    return data
 
ma_liste = [1, 2, 3]
print(ajouter_element(ma_liste, 4))  # Sortie : [1, 2, 3, 4]
print(ma_liste)  # Sortie : [1, 2, 3, 4]

Dans cette fonction ajouter_element, nous avons un side effect. La fonction ne renvoie pas seulement la liste mise à jour, mais modifie aussi la liste d'origine ma_liste. C'est un exemple typique de side effect en Python, et l'une des principales erreurs à éviter lors de l'écriture de code Python.

Les fonctions pures en Python

Une fonction pure, en revanche, est une fonction qui, pour la même entrée, produira toujours la même sortie et n'a pas de side effects. Ainsi, la pureté d'une fonction réside dans sa nature autonome, ce qui la rend prévisible et facile à tester.

# Exemple de fonction pure en Python
def ajouter_element_pure(data, element):
    nouvelle_liste = data.copy()
    nouvelle_liste.append(element)
    return nouvelle_liste
 
ma_liste = [1, 2, 3]
print(ajouter_element_pure(ma_liste, 4))  # Sortie : [1, 2, 3, 4]
print(ma_liste)  # Sortie : [1, 2, 3]

Dans cet exemple, ajouter_element_pure est une fonction pure. Elle crée une nouvelle liste basée sur l'entrée, puis ajoute le nouvel élément à cette liste. La liste originale ma_liste reste inchangée. Comprendre les caractéristiques des fonctions pures en Python est essentiel pour écrire un code robuste et plus facile à déboguer.

Utilisation des décorateurs pour contrôler les side effects

Les décorateurs en Python offrent un moyen puissant de modifier le comportement d'une fonction ou d'une classe. En enveloppant la fonction ou la classe, un décorateur peut exécuter du code avant ou après l'exécution de la fonction enveloppée, sans changer son code source.

# Exemple de décorateur en Python
def decorator_no_side_effects(func):
    def wrapper(*args, **kwargs):
        copie_data = args[0].copy()  # créer une copie des données
        return func(copie_data, *args[1:], **kwargs)
    return wrapper
 
@decorator_no_side_effects
def ajouter_element(data, element):
    data.append(element)
    return data
 
ma_liste = [1, 2, 3]
print(ajouter_element(ma_liste, 4))  # Sortie : [1, 2, 3, 4]
print(ma_liste)  # Sortie : [1, 2, 3]

Dans le code ci-dessus, le décorateur decorator_no_side_effects enveloppe la fonction ajouter_element. Il garantit qu'une copie des données est utilisée pour les opérations, laissant les données d'origine intactes. C'est ainsi que l'on utilise les décorateurs en Python pour contrôler les side effects.

Tester les side effects à l'aide des objets mocks

La bibliothèque unittest.mock en Python est un outil puissant pour écrire des tests. Elle vous permet de remplacer certaines parties de votre système en cours de test et de faire des assertions sur leur utilisation. La bibliothèque unittest.mock fournit également une classe Mock que vous pouvez utiliser pour créer un objet mock.

En matière de test unitaire Python, l'une des utilisations courantes des objets mocks est de tester le comportement du code dans des conditions contrôlées. Par exemple, lorsqu'une fonction appelle une autre fonction (qui peut avoir des side effects), un objet mock peut simuler la fonction appelée, ce qui permet d'éviter les side effects.

from unittest.mock import Mock
 
# Fonction d'origine que nous allons mocker
def ajouter_element(data, element):
    data.append(element)
    return data
 
# Objet mock
ajouter_element = Mock(side_effect=lambda data, element: data + [element])
 
# Test avec l'objet mock
ma_liste = [1, 2, 3]
print(ajouter_element(ma_liste, 4))  # Sortie : [1, 2, 3, 4]
print(ma_liste)  # Sortie : [1, 2, 3]

Dans cet exemple, ajouter_element est une fonction qui ajoute un élément à une liste, provoquant un side effect. Mais lorsque nous remplaçons ajouter_element par un objet mock, il simule la fonction sans le side effect. C'est un exemple d'utilisation des objets mocks en Python pour contrôler les side effects. La bibliothèque de test unitaire unittest.mock fournit également la fonction "patch", que vous pouvez utiliser pour remplacer les véritables objets dans votre code par des instances Mock lors des tests. La "patch" est ensuite automatiquement annulée après la fin du test.

Dans la partie suivante de l'article, nous discuterons davantage de la bibliothèque de patch en Python et explorerons plus d'exemples d'objets Mock en Python. Nous approfondirons également les caractéristiques des fonctions pures en Python, et nous examinerons plus d'exemples de décorateurs Python pour contrôler les effets secondaires. Restez à l'écoute pour apprendre comment utiliser le patch en Python et éviter les erreurs courantes lors de la gestion des effets secondaires en Python.

Utilisation de la bibliothèque de patch en Python

La bibliothèque de patch en Python, spécifiquement unittest.mock.patch, vous permet de contrôler la portée du mock et de déterminer quand l'objet original est remplacé et quand il est restauré. C'est particulièrement utile pour les tests unitaires où vous souhaitez mocker le comportement d'un objet pendant un test, puis faire en sorte que le mock soit automatiquement supprimé après la fin du test.

from unittest.mock import patch
 
def add_element(data, element):
    data.append(element)
    return data
 
def test_add_element():
    with patch('__main__.add_element', side_effect=lambda data, element: data + [element]):
        my_list = [1, 2, 3]
        print(add_element(my_list, 4))  # Output: [1, 2, 3, 4]
        print(my_list)  # Output: [1, 2, 3]
 
test_add_element()

Dans cet extrait de code, la fonction add_element est temporairement remplacée dans la portée de l'instruction with. Après l'exécution de la fonction de test, l'add_element original est restauré.

Structures de données immuables et effets secondaires

Les structures de données immuables sont un autre outil pour aider à contrôler les effets secondaires en Python. Par définition, un objet immuable ne peut pas être modifié après sa création. En Python, des exemples de structures de données immuables incluent les tuples, les chaînes de caractères et les frozensets.

Lors de l'utilisation de structures de données immuables, toute opération qui modifie les données va plutôt créer un nouvel objet. Cela contribue à éviter les effets secondaires car les données originales restent inchangées.

# Le tuple en Python est immuable
my_tuple = (1, 2, 3)
new_tuple = my_tuple + (4,)  # Créer un nouveau tuple
print(new_tuple)  # Output: (1, 2, 3, 4)
print(my_tuple)  # Output: (1, 2, 3)

Dans cet exemple, my_tuple est un tuple, qui est une structure de données immuable en Python. Lorsque nous essayons d'ajouter un élément, un nouveau tuple new_tuple est créé et my_tuple reste inchangé.

Conclusion

La gestion des effets secondaires en Python est un aspect important de l'écriture de code de haute qualité et maintenable. En comprenant et en utilisant les concepts de fonctions pures, de décorateurs et de structures de données immuables, ainsi qu'en maîtrisant l'utilisation de l'objet Mock et des bibliothèques de patch, vous pouvez garder votre code Python exempt d'effets secondaires inutiles. Cela rend non seulement votre code plus fiable, mais aussi plus facile à tester et à déboguer, ce qui se traduit par des applications Python de meilleure qualité.

FAQ

Abordons maintenant quelques questions fréquemment posées :

  1. Qu'est-ce qu'un effet secondaire en Python ?
    Un effet secondaire en Python est tout changement qu'une fonction apporte à l'état du programme ou à son propre état, en dehors de la valeur qu'elle renvoie. Les effets secondaires peuvent inclure la modification des variables globales ou statiques, la modification de l'objet d'origine, la production de sortie console ou l'écriture dans un fichier ou une base de données.

  2. Pourquoi faut-il éviter les effets secondaires en Python ?
    Les effets secondaires en Python, bien qu'ils ne soient pas toujours nuisibles, peuvent introduire des bogues difficiles à tracer et à corriger. Ils peuvent rendre votre code imprévisible et difficile à comprendre. En évitant les effets secondaires, vous rendez votre code plus stable, prévisible et plus facile à tester.

  3. Qu'est-ce qu'une fonction pure en Python ?
    Une fonction pure en Python est une fonction qui produit toujours la même sortie pour la même entrée et qui n'a pas d'effets secondaires. Les fonctions pures sont autonomes, ce qui les rend prévisibles et faciles à tester.