7 erreurs Python qui ralentissent votre code (et les correctifs importants)
Agence web » Actualités du digital » 7 erreurs Python qui ralentissent votre code (et les correctifs importants)

7 erreurs Python qui ralentissent votre code (et les correctifs importants)

Python est génial car c'est l'un des langages les plus faciles à apprendre et vous permet de créer des prototypes incroyablement rapidement. Cependant, cette commodité cache souvent des inefficacités majeures qui peuvent sérieusement ralentir les choses. Le script clair que vous avez écrit peut rapidement se transformer en un énorme goulot d'étranglement en termes de performances si vous n'y faites pas attention.

Si vous allez au-delà des scripts de base, vous devriez avoir une idée des pièges courants. Vous pouvez vous épargner du temps et des maux de tête en sachant simplement ce qu'il ne faut pas faire avant de commettre l'erreur.

Utiliser des listes pour la recherche d'adhésions

Lorsque vous vérifiez si une valeur est dans une liste (comme en utilisant if item in my_list), votre script analyse les éléments de manière séquentielle, ce qui signifie qu'il devra peut-être vérifier chaque élément si la cible est manquante ou située à la fin. C'est ce que nous appelons une recherche linéaire, et c'est une O(n) opération. Lorsque vos données commencent à croître, ce processus ralentit considérablement car le temps nécessaire pour trouver un élément augmente directement avec la taille de la liste.

En d’autres termes, si vous recherchez une valeur spécifique dans une liste d’un million d’éléments, Python devra peut-être comparer votre valeur cible à l’ensemble du million d’entrées avant de déterminer que l’élément n’est même pas là. Cela devient beaucoup plus lent lorsque les tests d'appartenance sont placés dans une autre boucle, généralement lorsque vous comparez deux ensembles de données.

Le problème de requête 'N+1'

Le N+1 Le problème de requête est probablement le problème de performances le plus courant que vous rencontrerez lors du développement d'applications, en particulier si vous utilisez un mappeur objet-relationnel ou si vous appelez des API externes. Cela se produit lorsque votre application exécute une requête initiale pour obtenir une liste d'éléments (le « 1 »), puis déclenche une requête de base de données distincte ou un appel d'API pour chaque élément de cette liste afin de récupérer les fichiers ou le contenu associés (le « N »).

Bien que la logique semble correcte dans votre code, vous parcourez simplement les éléments pour les traiter ; les problèmes de performances deviennent catastrophiques à mesure que vous évoluez. Il transforme ce qui devrait être une opération d'une fraction de seconde en une analyse lente qui peut réellement geler les threads du serveur. Le goulot d'étranglement n'est généralement pas la vitesse d'exécution de ces requêtes individuelles, mais le coût total de tous ces déplacements répétés vers la base de données.

Concaténation de chaînes dans une boucle

Les chaînes sont des objets immuables, donc une fois que vous créez une chaîne, vous ne pouvez pas la modifier sur place. Si vous exécutez une commande comme une chaîne += " data" à plusieurs reprises dans une boucle, le système doit allouer une toute nouvelle partie de mémoire suffisamment grande pour l'ancien contenu et le nouveau contenu combinés. Après cela, il copie la chaîne d'origine vers le nouvel emplacement, ajoute la nouvelle partie, puis supprime enfin l'ancien objet, en attendant le nettoyage.

Si vous effectuez cela des milliers de fois, vous provoquez une perte de mémoire massive et obligez le processeur à effectuer un travail lent et redondant qui ne s'adapte pas du tout lorsque vos ensembles de données deviennent volumineux.

Vous devez éviter de concaténer des chaînes directement à l'intérieur de la boucle. Utilisez plutôt une liste et la méthode .join().

Lire des fichiers entiers en mémoire

La lecture d'un fichier texte peut être considérée comme un hack en Python, mais n'essayez pas de lire des fichiers entiers en mémoire à l'aide d'une seule commande. Des méthodes comme f.read() ou f.readlines() sont pratiques pour les petits fichiers texte, mais ils se transforment en un désastre complet dès que vos données commencent à croître. Essayer de charger des gigaoctets de données en même temps est la principale raison pour laquelle vous obtenez des plantages immédiats de MemoryError.

La solution solide consiste à abandonner la suppression de fichiers au profit du streaming. Les objets fichier Python sont des itérateurs, vous pouvez donc les parcourir directement. Si vous utilisez un for line in file_handler: bloc, l'interpréteur lit une entrée à la fois, la traite et laisse immédiatement le garbage collector libérer cette mémoire avant de passer à la ligne suivante.

Boucles imbriquées inefficaces

Les boucles imbriquées inefficaces adorent se cacher à la vue de tous, se cachant souvent dans ce qui ressemble à une compréhension de liste innocente ou à une boucle for standard. Si vous parcourez une collection, puis pour chaque élément qu'elle contient, vous parcourez un autre ensemble de données, vous avez créé O(n) complexité. Le coût de calcul augmente quadratiquement à mesure que votre volume de données augmente.

Le moyen le plus courant et le plus efficace de résoudre ce goulot d'étranglement consiste à restructurer vos données avant même de commencer à itérer. Convertissez cette liste interne en un dictionnaire de recherche (carte de hachage) ou en un ensemble. Lorsque vous utilisez une carte de hachage, l'interpréteur calcule un code de hachage pour la clé et l'utilise pour accéder directement au compartiment de mémoire spécifique où se trouve cette valeur, évitant ainsi complètement la nécessité d'analyser d'autres éléments.

Ressources d'ouverture et de fermeture répétées

Ne laissez pas Python ouvrir et fermer constamment des ressources, comme des descripteurs de fichiers ou des connexions à une base de données, directement dans une boucle. Il est facile de coller un open() appel ou une demande de connexion dans une boucle for en raison de la syntaxe propre de Python, mais cela oblige le système d'exploitation à exécuter un processus d'établissement de liaison coûteux à chaque passage. Cette redondance tue les performances.

La solution simple ici consiste à retirer complètement toute la logique d’acquisition de ressources du bloc d’itération. Utilisez des gestionnaires de contexte, comme celui avec le open(...) instruction, en la plaçant avant même que la boucle ne commence. En ouvrant la ressource une fois, vous établissez la poignée de main nécessaire une seule fois. Vous pouvez ensuite sauter dans la boucle pour effectuer toutes les opérations de lecture ou d'écriture nécessaires à l'aide du handle déjà ouvert, ce qui réduit la surcharge du processeur et des entrées/sorties.

Ignorer les fonctions optimisées intégrées

Il est tentant de créer votre propre logique personnalisée simplement pour trier une liste, calculer un total ou filtrer certaines données. Vous pensez probablement que cela vous donne plus de contrôle. Cependant, le fait de s'appuyer sur ces boucles manuelles est la principale raison pour laquelle les applications Python s'exécutent lentement. Contrairement aux langages compilés qui transforment les boucles en code machine efficace, la nature interprétée de Python ajoute une surcharge à chaque itération. Cela signifie que chaque fois que vous exécutez une boucle for brute, vous rencontrez une surcharge majeure de l'interprète. Lorsque vous écrivez une boucle en Python pur, l'interpréteur doit constamment décoder les instructions, vérifier les types et gérer les appels de fonction pour chaque élément de votre collection.

Faites confiance à la bibliothèque standard et utilisez-la. Si vous commencez à écrire du code à la manière de Python, vous passerez un bien meilleur moment.


La chose importante à comprendre est que l’optimisation de votre code n’est pas une sorte de magie algorithmique. Il vous suffit de respecter et de comprendre comment le langage fonctionne réellement sous le capot. Un script lent n’est généralement qu’un tas de petites décisions non examinées qui se transforment en un énorme problème de performances.

La voie à suivre implique de réviser constamment votre propre code et de nettoyer les choses. La programmation est déjà assez difficile comme telle ; En apprenant où se trouvent ces pièges courants, vous vous assurerez de ne pas ajouter d'obstacles inutiles à votre propre flux de travail.