Communitygithub.com

mathias-mrsn/skills

Réviser et simplifier la conception du code Python.

Qu'est-ce que skills ?

Cette compétence est conçue pour les développeurs utilisant des agents d'IA comme Claude Code, Cursor ou Codex, qui souhaitent améliorer la qualité et la maintenabilité de leur code Python. Elle fournit un ensemble de principes, de règles et d'une procédure pour analyser et refactoriser le code Python, en se concentrant sur la simplification de la conception, l'application des idiomes pythoniques natifs, et l'évitement des abstractions inutiles ou des motifs de conception importés d'autres langages. La compétence couvre des sujets fondamentaux comme la syntaxe lisible, la gestion des références et des mutations, les nombres et leur vérité, les chaînes de caractères et leur interpolation, ainsi que les abstractions et le couplage. Elle inclut également des conseils sur l'utilisation appropriée des patrons de conception (Strategy, Command, Adapter, Façade, Proxy, State, etc.) sans imposer un catalogue rigide. La procédure guide l'utilisateur étape par étape, de l'identification du contexte jusqu'à la validation par les tests, en passant par la rédaction de la solution minimale et la comparaison des abstractions proposées. Des pièges courants sont identifiés pour éviter les erreurs fréquentes. En résumé, cette compétence permet à un agent IA d'agir comme un relecteur et un architecte logiciel expérimenté, aidant à produire un code Python plus propre, plus idiomatique et plus facile à faire évoluer.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/mathias-mrsn/skills/tree/HEAD/skills/technical/python/python-code-quality

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Qualité et Conception du Code Python

Quand l'utiliser

  • Écrire ou réviser du code Python qui fonctionne mais reste difficile à comprendre, étendre ou maintenir.
  • Remplacer une traduction directe de Java ou C++ par une construction propre au langage.
  • Choisir une abstraction, évaluer le couplage ou refactoriser après une nouvelle exigence.
  • Reconnaître l'intention d'un motif sans faire de son nom une obligation architecturale.
  • Ce périmètre concerne le raisonnement sur le code ; il ne prescrit ni un environnement de déploiement ni un framework.

Philosophie

Cherchez la plus petite construction qui résout le problème observé, et non la conception la plus générale imaginable. Un motif conserve son intention lorsque Python fournit déjà sa structure ; supprimer cette structure est une amélioration, et non un manque de sophistication. Une bonne abstraction rend le changement moins coûteux sans effacer les informations dont ses clients ont besoin. Synthèse opérationnelle de Thinking in Python, par Bruce Eckel ; corpus a8feb860. Respectez la version de Python effectivement supportée par le projet : la cible récente du corpus n'autorise aucune mise à niveau de version implicite.

Règles

Syntaxe lisible et noms prévisibles

  • Faites correspondre l'indentation à la structure réelle des blocs ; utilisez quatre espaces et ne mélangez pas les tabulations et les espaces.
  • Terminez les en-têtes de bloc par deux-points ; évitez les points-virgules et les instructions multiples qui compressent artificiellement le code.
  • Pour une instruction longue, utilisez la continuation entre parenthèses, crochets ou accolades plutôt qu'une coupure difficile à suivre.
  • Ne confondez pas blocs et portée : une affectation dans un if ou un for peut rester visible après le bloc.
  • Vérifiez qu'une affectation s'exécute sur chaque chemin avant que le nom ne soit utilisé ; l'indentation seule ne garantit pas sa liaison.
  • Utilisez snake_case pour les variables, fonctions, méthodes et fichiers ; PascalCase pour les classes ; majuscules pour indiquer les constantes.
  • Réservez l'exception des classes nommées comme des fonctions aux objets réellement utilisés de cette manière ; n'en faites pas un style général.
  • Ne déduisez pas une protection à l'exécution d'un nom en majuscule : il signale une intention au lecteur.
  • Conservez les conventions cohérentes du projet et utilisez son linter existant plutôt que de créer des règles locales arbitraires.

Références, valeurs et mutation

  • Lisez une affectation comme liant un nom à un objet ; elle ne copie pas l'objet.
  • Distinguez explicitement la réaffectation de la mutation : réaffecter un nom laisse ses alias précédents inchangés, tandis que muter l'objet peut les affecter tous.
  • Utilisez == pour l'égalité de valeur ; réservez is pour l'identité intentionnelle, notamment is None et autres singletons.
  • N'utilisez pas l'identité des chaînes ou des petits entiers comme test de valeur : les optimisations de partage ne fournissent pas cette garantie.
  • Une tranche ou une copie superficielle d'une liste recrée la structure externe, pas les éléments imbriqués ; vérifiez quels objets restent partagés.
  • Utilisez une copie profonde uniquement lorsque le besoin est aussi de dupliquer les objets contenus ; une affectation ne peut pas fournir cet isolement.
  • Ne remplacez pas mécaniquement x += y par x = x + y : pour une liste, la première forme la modifie sur place et la seconde en produit une nouvelle.
  • Pour les nombres immuables, comprenez que l'affectation augmentée relie le nom au résultat plutôt que de modifier le nombre partagé.
  • Préférez les données immuables lorsqu'elles n'ont pas besoin d'évoluer, sans confondre l'immuabilité d'un conteneur avec celle de son contenu.

Nombres et vérité

  • Distinguez /, division qui produit un flottant pour des opérandes entiers, de //, division arrondie vers l'infini négatif.
  • Testez les cas négatifs lors du portage : le quotient entier n'est pas une troncature vers zéro, et le reste % suit le signe du diviseur.
  • Ne supposez pas que les entiers Python ont une largeur fixe ; ils ne débordent pas comme les entiers bornés dans d'autres langages.
  • Séparez l'arithmétique entière exacte de l'arithmétique à virgule flottante ; n'importez pas les garanties de l'une dans l'autre.
  • Tenez compte de l'arrondi pair-pour-les-égalités avec round() et le formatage ; ne supposez pas que l'arrondi est toujours éloigné de zéro.
  • Utilisez des séparateurs _ dans les littéraux pour rendre les grands nombres lisibles sans changer leur valeur.
  • Utilisez += 1 et -= 1 pour incrémenter ou décrémenter ; Python n'a pas d'opérateurs ++ ou --.
  • Un booléen est aussi un entier : additionner des conditions peut compter les cas vrais, mais True ne constitue pas un type numérique distinct de int.
  • Préférez if items: à un test de longueur lorsque la question est simplement de savoir si le conteneur est vide.
  • Rappelez-vous que la valeur de vérité d'un objet peut être définie par __bool__() ou, à défaut, __len__().
  • and et or court-circuitent et renvoient un opérande, pas nécessairement un booléen ; n'annotez pas leur résultat en vous basant uniquement sur leurs noms.
  • Utilisez x or default uniquement si toutes les valeurs fausses doivent être remplacées ; préservez zéro ou le texte vide avec un test explicite None.
  • Pour les masques, distinguez les opérateurs bit à bit des opérateurs logiques ; ~x n'inverse pas un mot de largeur fixe.
  • Ne vous attendez pas à ce que @ effectue un produit sur les nombres intégrés : la multiplication matricielle dépend du type et diffère de la syntaxe de décorateur.

Chaînes et interpolation

  • Les chaînes sont immuables : conservez le résultat d'une transformation s'il doit être réutilisé.
  • Choisissez des guillemets qui limitent l'échappement ; utilisez des triples guillemets lorsque les sauts de ligne font partie du texte.
  • Utilisez une chaîne brute pour les barres obliques inverses littérales, en tenant compte de l'impossibilité de terminer ce littéral par une barre oblique inverse seule.
  • Utilisez les opérations intégrées de recherche, division, remplacement et jointure plutôt que des boucles de manipulation de caractères sans besoin particulier.
  • N'utilisez pas strip, lstrip ou rstrip pour supprimer un préfixe exact : leur argument désigne des caractères, pas une sous-chaîne indivisible.
  • Utilisez removeprefix() et removesuffix() pour supprimer une sous-chaîne exacte de début ou de fin lorsque la version cible les supporte.
  • + ne convertit pas implicitement un nombre en chaîne ; utilisez une conversion explicite ou une f-string.
  • Préférez une f-string pour produire du texte ordinaire ; son expression est évaluée et son résultat rendu immédiatement.
  • Utilisez la spécification de format pour la précision, la largeur et l'alignement ; !r affiche une représentation utile pour le diagnostic, et = étiquette une expression.
  • Ne réécrivez pas les anciennes utilisations de % ou str.format() simplement parce qu'elles existent : elles restent valides, et une migration doit améliorer le code concerné.
  • Envisagez une t-string uniquement si la version cible la supporte et qu'un consommateur a besoin de distinguer les littéraux des valeurs avant l'assemblage.
  • Une t-string produit un objet structuré, pas une chaîne finie ; le consommateur doit effectivement citer, échapper ou rejeter les interpolations selon son domaine.
  • Ne promettez pas de sécurité basée uniquement sur la syntaxe d'interpolation, et ne réutilisez pas un rejet de caractères illustratif comme protection générale.
  • Un modèle consommateur ne doit pas supposer une alternance stricte entre texte et valeurs : l'itération peut omettre les littéraux vides.

Abstractions, dépendances et cohésion

  • Avant d'ajouter une nouvelle classe, vérifiez si une fonction, un dictionnaire, un module ou un protocole de langage suffit.
  • Une classe qui héberge uniquement une méthode n'est pas justifiée par cette seule organisation.
  • Nommez l'axe de changement en fonction des exigences réelles ; les changements répétés au même endroit sont une preuve plus utile qu'une flexibilité anticipée.
  • Séparez ce qui change de ce qui reste stable ; ne construisez pas de mécanisme de variation lorsque rien ne varie.
  • Traitez un idiome, une solution spécifique et un motif général comme des étapes possibles, pas comme une échelle à gravir.
  • Pour chaque abstraction, identifiez les détails qu'elle efface et confirmez qu'aucun client légitime n'a besoin de les récupérer.
  • Examinez également le travail caché derrière l'interface : la copie, le tri ou l'accès indirect peuvent devenir dominants à la taille réelle des données.
  • Évaluez le couplage par ce qu'une partie connaît : détails du parent, classe concrète, interface, signature d'appel ou une simple valeur.
  • Ne confondez pas une dépendance typée avec l'absence de dépendance : un Protocol évite l'héritage déclaré, mais ses membres restent un contrat.
  • Préférez une signature de fonction lorsque le client n'a besoin que d'une seule opération ; ne créez pas d'interface multi-méthodes sans utilité.
  • Un motif déplace souvent une dépendance plutôt que de la supprimer ; localisez la partie qui doit encore nommer les classes concrètes.
  • Acceptez une dépendance concrète lorsqu'elle est délibérément placée à un point d'assemblage peu coûteux à modifier.
  • Ne plongez pas dans les détails internes d'objets obtenus indirectement ; demandez le service requis via une interface explicite.
  • Regroupez les éléments servant le même sujet dans un composant ; séparez les idées qui peuvent évoluer indépendamment.
  • Préférez la composition pour faire varier un collaborateur sans hériter de ses détails d'implémentation ; conservez l'héritage si la substituabilité est réelle.
  • Vérifiez qu'un sous-type répond aux attentes des clients du type de base, et pas seulement aux noms de ses méthodes.
  • Extrayez la duplication qui exprime la même intention pour la même raison ; la ressemblance accidentelle ne suffit pas.
  • Rendez les cas courants simples, les cas rares possibles et les interfaces cohérentes ; une exception comportementale impose un coût de mémorisation.
  • Supprimez une couche si sa suppression n'élimine ni le comportement nécessaire ni un isolement utile ; choisissez entre deux solutions suffisantes par leur simplicité.
  • Rendez les calculs purs lorsque c'est possible : des entrées égales produisent le même résultat, sans changements observables pour le monde extérieur.

Refactoriser selon les deux axes d'extension

  • Distinguez l'ajout d'un type de l'ajout d'une opération ; une conception favorise souvent l'un au détriment de l'autre.
  • Un match qui énumère toutes les sous-classes d'une famille ouverte nécessite la maintenance de la liste et peut ignorer silencieusement un nouveau type.
  • Si l'opération ne regroupe que par classe exacte, un dictionnaire indexé par type(obj) évite cette énumération.
  • Ne traitez pas ce remplacement comme purement syntaxique : une sous-classe obtient alors sa propre clé, contrairement à un motif de classe parente.
  • Pour les groupes créés à la première occurrence, defaultdict(list) fournit une nouvelle liste ; une annotation de dictionnaire ne crée pas ce comportement.
  • Laissez une opération uniforme comme une fonction ordinaire qui lit les attributs pertinents ; la répartition n'ajoute rien si l'algorithme ne varie pas par type.
  • Gardez les méthodes sur une hiérarchie petite et contrôlée lorsque chaque type porte naturellement ses réponses et que les opérations restent peu nombreuses.
  • Considérez functools.singledispatch pour ajouter des opérations qui varient par type sans modifier une hiérarchie externe ou répartir de nouvelles opérations dans chaque classe.
  • Avec singledispatch, l'implémentation est sélectionnée sur le premier argument et peut être héritée selon le MRO ; ce n'est pas une table de classes exactes.
  • Une réponse par défaut doit être réellement valide pour les types non enregistrés ; sinon, levez NotImplementedError pour rendre l'omission visible.
  • N'ajoutez pas de méthodes à partir de modules arbitraires : cela oblige les lecteurs à chercher le comportement d'une classe en dehors de sa définition.
  • Mesurez une refactorisation par les emplacements modifiés et les données préservées, pas seulement par le nombre de lignes supprimées.

Utiliser le vocabulaire des motifs sans catalogue imposé

  • Décrivez d'abord le problème : création, adaptation d'interface, sélection d'algorithme, notification, un arbre ou restauration d'état ; le nom vient après.
  • Réduisez Strategy et Command à passer ou stocker une fonction lorsque cela suffit ; une chaîne peut être une liste de fonctions essayées dans l'ordre.
  • Reconnaissez qu'un module partagé, une table de classes ou le protocole d'itération peuvent déjà remplir l'intention de Singleton, Factory ou Iterator.
  • Distinguez Adapter, qui convertit une interface, Façade, qui simplifie un sous-système, et Proxy, qui contrôle l'accès à un autre objet.
  • Distinguez State, qui change le comportement selon l'état, d'un automate d'état, qui sélectionne des transitions en réponse à des entrées.
  • Distinguez l'injection d'un collaborateur de l'extérieur de sa recherche dans un registre ; les deux déplacent le contrôle sans exposer les dépendances de la même manière.
  • Gardez les noms architecturaux, de persistance, de messagerie et de cloud au niveau d'intention décrit : leur présence dans un catalogue n'établit aucune recette d'implémentation.
  • Ne déduisez pas une recommandation de déploiement, de récupération ou de cohérence d'une simple mention de Circuit Breaker, Saga, Repository ou Event Sourcing.

Procédure

  1. Identifiez le Python cible, les conventions et les vérifications déjà utilisées ; enregistrez le comportement à préserver.
  2. Énoncez une exigence concrète et l'axe de changement observé ; listez les composants affectés aujourd'hui.
  3. Vérifiez les références partagées, les valeurs de vérité, les opérations numériques et les transformations de chaînes impliquées.
  4. Écrivez la solution minimale, puis comparez l'abstraction proposée : détails effacés, travail caché et dépendances déplacées.
  5. Testez l'ajout d'un type et l'ajout d'une opération séparément ; notez les changements requis dans chaque variante.
  6. Refactorisez par étapes tout en conservant les tests comportementaux et les comptes de données entrantes et sortantes.
  7. Supprimez les couches devenues inutiles, puis exécutez les tests et vérifications du projet sur ses versions supportées.

Pièges

  • Un programme peut produire un total plausible après avoir perdu de nouveaux types : vérifiez la préservation des données, pas seulement la présentation du résultat.
  • Une branche de repli ou une implémentation par défaut peut cacher une extension oubliée ; préférez un échec explicite si aucune réponse neutre n'est correcte.
  • Généraliser avant d'avoir plusieurs cas réels crée des options sans preuve d'utilité ; rejetez le cadre spéculatif.
  • Une abstraction courte peut cacher un coût élevé ou effacer un détail indispensable ; compter les lignes ne suffit pas pour juger sa qualité.
  • La syntaxe récente et les API du corpus ne sont pas nécessairement disponibles dans un projet plus ancien ; n'augmentez pas sa version minimale de Python pour les adopter.

Vérification

  • Chaque nouvelle classe ou interface protège un besoin nommé ; aucun motif n'est présent uniquement pour son nom.
  • Les chemins conditionnels lient leurs variables avant utilisation ; l'identité, la mutation et l'égalité ne sont pas confondues.
  • Zéro, les valeurs vides, None, les nombres négatifs et les données imbriquées ont été testés lorsque leur distinction est importante.
  • Les nouvelles sous-classes ne disparaissent pas silencieusement, et les tests de classe exacte ou d'héritage sont distincts.
  • Les motifs de conception sont classés par intention et non par nom de catalogue ; leur implémentation reste minimale.
  • Les dépendances, coûts cachés et axes de changement ont été documentés avant de figer l'abstraction.

Skills associés