6 erreurs en Python à éviter lors de la programmation
En tant que nouveaux programmeurs, nous avons tous fait des erreurs. Mais certaines erreurs sont inévitables en raison de la méconnaissance de la nature d'un langage de programmation. J'ai donc compilé certaines des erreurs les plus courantes que vous pourriez commettre en programmant en Python.
6. Modifier une liste tout en la parcourant
Modifier une liste Python pendant que vous la parcourez peut sembler une astuce intelligente, mais c'est un piège classique en Python ! Cette erreur sournoise peut entraîner un comportement inattendu, comme sauter des éléments ou se retrouver avec une liste complètement désordonnée.
Disons que nous avons une liste de nombres, et que nous voulons supprimer tous les nombres inférieurs à 5. Ça semble simple, n'est-ce pas ?
numbers = [1, 2, 6, 3, 7, 4, 8, 5]
print("Original list:", numbers)
for number in numbers:
if number < 5:
numbers.remove(number)
print("Modified list (oops!):", numbers)
Si vous exécutez ce code, vous constaterez que le nombre 2 reste dans la liste.
Mais pourquoi ? Dans ce code, nous essayons de parcourir numbers et de supprimer les éléments inférieurs à 5. Cependant, lorsque nous supprimons un élément, la liste se réduit et les indices des éléments suivants changent.
Cela peut provoquer un saut d'éléments dans la boucle. Par exemple, lorsque 1 est supprimé, 2 prend la position précédente de 1, mais le compteur interne de la boucle a déjà avancé, ce qui peut entraîner le saut de 2.
La manière la plus sûre et la plus courante de modifier une liste en fonction de ses éléments est d’itérer sur une copie de la liste ou de créer une nouvelle liste avec uniquement les éléments que vous souhaitez conserver. Dans l’approche par copie, nous créons une copie superficielle de la liste avant l’itération. De cette façon, vous pouvez supprimer des éléments de la liste originale sans affecter le processus d’itération.
numbers = [1, 2, 6, 3, 7, 4, 8, 5]
print("Original list:", numbers)
for number in numbers[:]: # Iterate over a slice (copy) of the list
if number < 5:
numbers.remove(number)
print("Modified list (fixed with copy):", numbers)
En utilisant numbers[:], nous créons un slice de la liste numbers, qui est une copie superficielle. La boucle for itère ensuite sur cette copie, totalement indépendante de toute modification que nous apportons à la liste originale numbers.
Souvent, une manière plus pythonique de réaliser cela consiste à créer une nouvelle liste contenant uniquement les éléments que vous souhaitez conserver. Cette approche évite toute modification sur place et est souvent plus efficace.
numbers = [1, 2, 6, 3, 7, 4, 8, 5]
print("Original list:", numbers)
filtered_numbers = [number for number in numbers if number >= 5]
print("Filtered list (new list method):", filtered_numbers)
Dans ce code, nous utilisons la compréhension de liste pour créer une nouvelle liste à partir de l'originale qui contient les éléments souhaités. Cela permet également de conserver la liste originale intacte.
5. Conflits de noms
Choisir des noms de variables et de fichiers peut sembler être un détail mineur jusqu'à ce que Python se retourne contre vous (au sens figuré). Mal nommer les choses peut entraîner des bugs étranges, des fonctionnalités masquées et des heures de confusion. Il existe deux façons courantes pour que cela se produise : utiliser des mots-clés de Python ou des noms intégrés comme les vôtres, et nommer accidentellement vos scripts comme des modules Python existants.
Python ne vous empêchera pas de nommer une variable list, str ou même sum. Mais juste parce que vous le pouvez, cela ne signifie pas que vous devriez le faire.
list = [1, 2, 3]
print(list)
Ce code s'exécutera sans aucun problème. Cependant, si vous ajoutez une autre ligne contenant la fonction list() en Python, vous obtiendrez une erreur.
list = [1, 2, 3]
print(list)
list = list('123')
C'est parce que vous avez écrasé la fonction intégrée list() avec votre propre objet liste. Lorsque vous essayez d'appeler list('123'), Python pense que vous essayez d'appeler votre propre liste, et non la fonction intégrée. Donc, utilisez toujours des noms descriptifs qui ne sont pas en conflit avec les fonctionnalités principales de Python.
numbers = [1, 2, 3]
converted = list('123')
Non seulement cela vous aidera à éviter d'utiliser des mots-clés réservés, mais c'est également une bonne pratique pour la lisibilité du code et le débogage. Il en va de même pour le nommage de vos fichiers de script. Supposons que vous créiez un fichier nommé random.py pour expérimenter avec des nombres aléatoires :
# random.py
import random
print(random.randint(1, 10))
Mais lorsque vous exécuterez ceci, vous verrez probablement une erreur se plaignant que « module 'random' n'a pas d'attribut 'randint' ». Python importe votre propre fichier (random.py) au lieu du module random réel de la bibliothèque standard. C'est le classique bug de masquage.
Évitez de nommer vos scripts d'après des modules existants tels que random.py, math.py, os.py ou json.py. Et si vous le faites par accident, n'oubliez pas de supprimer les dossiers .pyc ou __pycache__ correspondants après avoir renommé votre fichier.
4. Arguments de fonction modifiables
Python vous permet de définir des valeurs par défaut pour les arguments de fonction, ce qui est très pratique. Mais lorsque ces valeurs par défaut sont mutables (comme les listes ou les dictionnaires), vous risquez de tomber dans un piège à bugs sournois.
L'objet mutable par défaut n'est créé qu'une seule fois, lorsque la fonction est définie, et non à chaque appel. Cela signifie que si cet objet est modifié, ce changement persiste pour l'appel suivant.
def add_item_to_list(item, item_list=[]):
item_list.append(item)
return item_list
print(add_item("apple"))
print(add_item("banana"))
Lorsque vous exécutez ce code, vous pourriez vous attendre à ce que chaque appel à add_item_to_list() commence avec une liste vide et nouvelle si vous n'en fournissez pas une. Cependant, le résultat montrera quelque chose d'inattendu :
Nous avons appelé la fonction deux fois. Alors, pourquoi "apple" est-il toujours là lors du deuxième appel ? La liste item_list n'est créée qu'une seule fois, donc chaque appel de la fonction continue de modifier la même liste. Cela conduit à un comportement surprenant et très bogué. Pour corriger cela, vous pouvez utiliser None comme valeur par défaut et créer la liste à l'intérieur de la fonction :
def add_item_to_list(item, item_list=None):
if item_list is None:
item_list = []
item_list.append(item)
return item_list
print(add_item("apple"))
print(add_item("banana"))
En définissant par défaut sur None, une nouvelle liste est créée à chaque fois que la fonction est appelée.
3. Manipuler les fichiers de la mauvaise manière
Travailler avec des fichiers en Python est assez facile. Mais il y a une erreur courante qui peut se glisser dans votre code : ne pas fermer le fichier. Cela peut sembler anodin, mais laisser des fichiers ouverts peut entraîner des fuites de mémoire, la corruption de fichiers ou des fichiers verrouillés que d'autres programmes ne peuvent pas accéder.
file = open('data.txt', 'r')
content = file.read()
print(content)
# Forgot to close the file!
Laisser des fichiers ouverts peut causer de sérieux problèmes. Cela peut même empêcher que le fichier soit supprimé ou modifié par d'autres processus. C'est pourquoi vous devez toujours vous assurer de fermer le fichier après avoir terminé vos opérations. Vous pouvez le faire en utilisant la fonction close().
file = open('data.txt', 'r')
content = file.read()
print(content)
file.close()
Une autre bonne pratique consiste à utiliser l'instruction with de Python, qui se charge automatiquement de fermer le fichier pour vous.
with open('data.txt', 'r') as file:
content = file.read()
print(content)
Le bloc with gère tout le nettoyage en arrière-plan. Dès que vous quittez le bloc, Python ferme le fichier pour vous. Cela vous permet de ne pas avoir à vous soucier de fermer le fichier manuellement.
2. Importer Tout Depuis Un Module
En tant que débutant, il est tentant de simplement mettre un from module import * en haut de votre script et de considérer que c'est suffisant. Mais à mesure que votre code se développe, ce raccourci devient une recette pour le désastre.
from math import *
from custom_math import *
print(sqrt(16))
Que se passe-t-il si à la fois math et custom_math définissent une fonction appelée sqrt() ? Laquelle est appelée ? Python ne vous le dit pas. Il remplace simplement silencieusement celle définie précédemment.
Vous pourriez utiliser une fonction complètement différente sans vous en rendre compte. De plus, dans de grandes bases de code, le débogage devient un cauchemar. On perd la trace de l'origine des fonctions ou des classes. C'est pourquoi vous devriez être explicite avec vos importations.
import math
import custom_math
print(math.sqrt(16))
print(custom_math.sqrt(16))
Ou même comme ça :
from math import sqrt as m_sqrt
from custom_math import sqrt as c_sqrt
print(m_sqrt(16))
print(c_sqrt(16))
Explicite vaut mieux qu'implicite (une idée centrale du Zen de Python). Vous savez toujours ce que vous utilisez et d'où cela vient.
1. Gestion appropriée des exceptions
Même le code soigneusement conçu peut rencontrer des problèmes inattendus. Ceux-ci sont appelés exceptions en Python. Les gérer avec élégance est ce qui rend votre programme robuste. Cependant, de nombreux débutants abordent la gestion des exceptions avec une mentalité de « tout attraper », en utilisant des clauses except générales. Cela peut faire taire l'erreur pour l'instant, mais cela rend votre code fragile, confus et difficile à déboguer.
try:
result = 10 / 0
except:
print("Something went wrong!")
Cela capture tout. Même les erreurs auxquelles vous ne vous attendiez pas ou que vous ne vouliez pas gérer. Les erreurs de syntaxe, les interruptions clavier, les fautes de frappe, les imports cassés. Python les acceptera toutes avec ce except générique. Ce n'est pas idéal. À la place, soyez précis sur ce que vous capturez :
try:
result = 10 / 0
except ZeroDivisionError:
print("You can't divide by zero!")
Vous ne capturez que l'erreur que vous attendez. Si autre chose ne va pas, Python la soulèvera toujours. Ainsi, vous n'ignorerez pas silencieusement de vrais bogues. Vous pouvez également gérer plusieurs exceptions de manière intelligente :
try:
value = int(input("Enter a number: "))
result = 10 / value
except ValueError:
print("That's not a valid number!")
except ZeroDivisionError:
print("You can't divide by zero!")
Évitez except : tout seul. Si vous devez attraper toutes les exceptions (rarement), utilisez except Exception : et gérez-les quand même avec soin. En traitant les exceptions de manière spécifique, votre code devient plus prévisible, plus facile à déboguer et capable de fournir des retours plus significatifs.
En évitant ces erreurs, vous introduirez moins de fautes, ce qui signifie moins de débogage de votre code Python. Si vous débutez en Python et cherchez des ressources, il existe de nombreux jeux qui enseignent Python. En dehors de cela, vous pouvez également réaliser des projets comme un gestionnaire de dépenses ou créer des scripts simples.




