Le PEP8, en résumé 22


Internet, c’est la culture du TL;DR, donc plutôt je vais faire une petite synthèse des trucs les plus importants du PEP8, comme ça si vous avez la flemme de le lire, au moins vous aurez l’essentiel.

Ce texte liste les règles stylistiques recommandées, invitant toute la communauté Python à écrire un code de la même façon.

Je vais également y ajouter des éléments de style qui ne sont pas dedans, mais que j’ai pu constater comme étant les choix les plus courants dans les sources que j’ai pu lire.

Espaces

Les opérateurs doivent être entourés d’espaces.

Il faut faire ceci :

variable = 'valeur'
 
ceci == cela
 
1 + 2

Et non:

variable='valeur'
 
ceci==cela
 
1+2

Il y a deux exceptions notables.

La première étant qu’on groupe les opérateurs mathématiques ayant la priorité la plus haute pour distinguer les groupes :

a = x*2 - 1
b = x*x + y*y
c = (a+b) * (a-b)

La seconde est le signe = dans la déclaration d’arguments et le passage de paramètres :

def fonction(arg='valeur'): #  ça c'est ok
    pass
 
resultat = fonction(arg='valeur') #  ça aussi

On ne met pas d’espace à l’intérieur des parenthèses, crochets ou accolades.

Oui :

2 * (3 + 4)
 
def fonction(arg='valeur'):
 
{str(x): x for x in range(10)}
 
val = dico['key']

Non :

2 * ( 3 + 4 )
 
def fonction( arg='valeur' ):
 
{ str(x): x for x in range(10) }
 
val = dico[ 'key' ]

On ne met pas d’espace avant les deux points et les virgules, mais après oui.

Oui :

def fonction(arg1='valeur', arg2=None):
 
dico = {'a': 1}

Non :

def fonction(arg='valeur' , arg2=None) :
 
dico = {'a' : 1}

Lignes

Une ligne doit se limiter à 79 charactères. Cette limite, héritée des écrans touts petits, est toujours en vigeur car il est plus facile de scanner un code sur une courte colonne qu’en faisant des aller-retours constant.

Si une ligne est trop longue, il existe plusieurs manières de la racourcir :

foo = la_chose_au_nom_si_long_quelle_ne_tient_pas_sur(
          une, 
          carte, 
          de, 
          munchkin)

Ici l’indentation entre le nom de la fonction et des paramètres est légèrement différente pour mettre en avant la distinction.

Une variante :

foo = la_chose_au_nom_si_long_quelle(ne, tient, pas, sur, carte 
                                     une, de, munchkin)

Si c’est un appel chainé, on peut utiliser \ pour mettre à la ligne :

queryset = ModelDjangoALaNoix.objects\
                             .filter(banzai=True)\
                             .exclude(chawarma=False)

Si c’est une structure de données, on peut se la jouer langage fonctionel de la vibes du flex :

chiffres = [
    1, 2, 3,
    4, 5, 6,
]
 
contacts = {
    'Cleo': (),
    'Ramses': (
        ('maman', '0248163264'),
        ('papa', '01234567890'),
        ('mamie', '55555555'),
        ('momie', '066642424269')
    )
}

Séparer les fonctions et les classes à la racine d’un module par 2 lignes vides. Les méthodes par 1 ligne vide. Parfois je triche et je fais 3 et 2 au lieu de 2 et 1.

Les imports de plusieurs modules doivent être sur plusieurs lignes :

import sys
import os

Et non :

import sys, os

Bien sûr, ce n’est pas valable pour from x import z.

Souvenez-vous qu’on peut utiliser les parenthèses pour diviser de longues lignes. Par exemple :

from minibelt import (dmerge, get, iget, normalize, 
                      chunks, window, skip_duplicates, flatten)

Idem pour les chaînes très longues :

s = ("Les chaînes Python sont automatiquement"
     "concaténées par la VM si elles sont "
     "uniquement séparées par des espaces "
     "ou sauts de lignes.")

Pour en revenir aux lignes d’import, on doit les ordonner ainsi :

  • Import de module > import du contenu du module
  • Import de la lib standard > import de libs tierces parties > import de votre projet

Exemple :

import os  # import module de la lib standard
import sys # on groupe car même type 
 
from itertools import islice  # import du contenu du module
from collections import namedtuple # import groupe car même type
 
import requests # import lib tierce partie
import arrow # on groupe car même type
 
from django.conf import settings # tierce partie, contenu du module
from django.shortcuts import redirect # on groupe car même type
 
from mon_projet.mon_module import ma_bite # mon projet

Format du fichier

Indentation : 4 espaces. Pas de tab. C’est tout. Ce n’est pas du PEP8, c’est juste que les codes qui utilisent les tabs sont en (très très très très très très très très) grande minorité dans la communauté Python. Donc faites pas chier. Sinon on vous attend à la sortie de l’école. Tous.

Encoding : UTF8 ou ASCII (ce qui est de l’UTF8 de toute façon).

Docstrings

(c.f. PEP257)

On utilise toujours des triples quotes :

def fonction_avec_docstring_courte():
    """Résumé en une ligne."""
    pass

Si la docstring est longue (elle peut être très très très longue si vous le souhaitez) :

def fonction():
    """Résumé en une ligne suivi d'une line vide.
 
    Description longue de la fonction qui 
    se termine par une ligne vide puis une
    triple quotes sur sa propre ligne.
 
    """

Noms de variables

Lettres seules, en minuscule : pour les boucles et les indices.

Exemple :

for x in range(10):
    print(x)
 
i = get_index() + 12
print(ma_liste[i])

Lettres minuscules + underscores : pour les modules, variables, fonctions et méthodes.

une_variable = 10
 
def une_fonction():
    return locals() or {}
 
class UneClasse:
    def une_methode_comme_une_autre(self):
        return globals()

Lettres majuscules + underscores : pour les (pseudo) constantes.

MAX_SIZE = 100000  # à mettre après les imports

Camel case : nom de classe.

class CeciEstUneClasse:
    def methodiquement(self):
        pass

Si le nom contient un acronyme, on fait une entorse à la règle :

class HTMLParserCQFDDDTCCMB:
    def methodiquement(self):
        pass

On n’utilise PAS le mixedCase.

22 thoughts on “Le PEP8, en résumé

  • ko

    Bonsoir,

    Premier exemple de code à ne pas faire : il faut enlever les espaces autour du ‘+’ sinon c’est pas logique.

    « On ne met pas d’espace à l’intérieur des parenthèses, crochets ou accolades. » → Bloc « Non » : il y a une balise HTML qui se balade.

    Enfin, tout en bas pour les noms de classes avec acronyme (un seul ‘c’), l’exemple va à l’encontre de ce qui est dit dans le PEP8 : « Note: When using abbreviations in CapWords, capitalize all the letters of the abbreviation. Thus HTTPServerError is better than HttpServerError. » (http://www.python.org/dev/peps/pep-0008/#descriptive-naming-styles)

    Voilà, voilà. Merci pour cet article, les précédents et ceux à venir =)

  • G-rom

    Pour ceux qui ne le savent pas, et pour éviter qu’ils se posent des questions sur un truc qu’en fait ils connaissent déjà : lettre + underscore = snake_case

    Sinon petite correction : on n’utilise PAS… (dernière phrase)

  • Toniob

    La limite à 80 caractères n’a pas été poussée à 100 récemment ? 80 ça fait court tout de même.

  • Sam Post author

    Le PEP8 dit 80, mais si ta productivité est plus importante avec 100, utilise 100. Suivre le PEP8 aveuglément n’apporte rien, l’idée c’est d’avoir une solide base commune, pas de faire du bondage.

    Après moi 80 me va très bien, ça m’oblige à écrire un code propre et à éviter les one-liners à ralonge. Si je dépasse une ou deux fois dans un fichier de 500 lignes, c’est pas grave.

  • roro

    Les espaces, c’est bien à la lecture (encore que quoi queue…)
    Mais ça fait chier à la rédaction.

  • JeromeJ

    Dans l’exemple correct “{str(x) : x for x in range(10)}” tu as laissé l’espace avant les deux points :(

  • Tony

    Certains editeurs integrent des controles pendant l’édition. Mais c’est souvent pas super parametrable ou deconnant. L’utilisation d’outil comme flake8/pep8 est donc de toutes façons fortement recommandées.

  • herison

    Il y a pep8 qui est un outil pour voir si le code répond à la PEP8, mais j’utilise pylint qui est moins sensible à la PEP8 et affiche d’autre erreur comme les variables non utilisées ou les partie de code demandant un refactoring.

  • kontre

    @Tony: souvent les éditeurs utilisent déjà pep8/flake8 pour afficher les warnings, et c’est vrai que c’est super pratique.
    @herison: pylint est trop lent pour fonctionner en temps réel contrairement à pep8. Il faut aussi plus de trucs (avec parfois un tas de faux positifs quand il ne trouve pas une librairie).

    Il y a l’utilitaire autopep8 qui permet de corriger automatiquement une bonne partie des manquements au pep8.

  • Marien

    Comme je reviens sur ce formidable article et que je compare en même temps avec le document “officiel”, je viens corriger deux petits trucs (je sais pas si ce sera pris en compte mais au moins ce sera là) :

    – Les commentaires inline doivent être séparés par deux espaces du code et suivi d’un seul espace (c’est le cas dans certains exemples mais pas toujours)
    – Le from collections on namedtuple c’est fait exprès ? Parce que c’était bon dans l’article stocké par mon agrégateur de flux RSS ce qui veut dire que ça a été changé après la publication de l’article (en fait je crois que vous n’avez pas corrigé le bon “import”)

    J’en avais d’autres mais ça a été corrigé depuis le temps :)

  • Sam Post author

    Merci Marien. Pour les commentaires, je ne respecte pas cette convention, donc je ne vais pas la recommander. Pour le from, je corrige tout de suite.

  • Biganon

    Ce que tu appelles “camel case”, c’est pas plutôt du Pascal case ? Sauf erreur, le camel case commence par une minuscule, en tout cas par convention c’est ce qu’on désigne par camel case en informatique.

  • Dudulle

    Dans le monde, il y a plus de personnes n’ayant pas accès à l’eau potable, les autres doivent donc cesser l’opération barbare de filtrer et désinfecter l’eau qu’ils boivent.

    Si cette affirmation vous choque, pourquoi prétendre qu’il faut utiliser l’espace plutôt que la tabulation comme caractère d’indentation, sous prétexte que c’est la pratique la plus courante (courante, eau non-potable, gag.) ?

    En effet, la tabulation a quelques avantages sur l’espace :

    1) diminution de la taille du fichier. (donc diminution du temps d’interprétation, j’en parle plus bas)

    2) possibilité de modification de l’aspect dans l’éditeur, sans modifier le fichier (et sans modifier l’aspect des autres espaces)

    3) c’est PUTAIN de fait pour. Quel est votre avis sur les gens qui continuent à écrire du HTML avec des

    <

    div class=”menu”> quand

    <

    nav> existe ?

    Maintenant, des hypothèses plus rationnelles que “les chinois ont les yeux bridés, c’est le peuple majoritaire dans le monde, il doit y avoir une raison, je vais plisser les yeux au cas où.”:

    – La sécurité lors de la rédaction : Un backspace malheureux, et c’est une indentation qui est supprimée. Le choix de l’espace pourrait être une mesure de sécurité dans un langage où la portée d’un block ou d’une boucle est définie par l’indentation : avec la tabulation, une erreur et le comportement du programme varie, alors qu’avec l’espace, l’erreur est signalée lors de l’interprétation. Sauf que : l’indentation “intelligente” des éditeurs de texte modernes supprime cet avantage hypothétique. Pour en profiter, il va falloir taper vos espaces 1 par 1.

    – la vitesse d’interprétation : j’écris plus haut que moins de caractères à lire, c’est forcément plus rapide à interprêter. Sauf qu’un parser de code un peu optimisé s’attend à rencontrer certains caractères en premier, et si votre interprêteur de Python s’attend à lire des espaces en début de ligne plutôt que des tabulations, il se peut qu’il soit plus rapide dans ce cas. A vos chronomètres.

    Jusqu’ici, aucun argument contre l’utilisation de la tabulation dans l’indentation du code Python ne m’ayant convaincu, j’assume mon statut de connard obtu et continue à indenter avec la tabulation.

  • Sam Post author

    C’est une guerre sans fin. Le PEP 8 n’est pas là pour donner raison, il est là pour donner un standard arbitraire justement pour mettre fin aux débats inutiles et dépenser son énergie dans le boulot plutôt que la discussion sur l’indentation.

  • Dudulle

    J’avoue être un peu déçu que la seul ‘justification’ soit simplement arbitraire.

    I want to believe !

  • atrament

    Nah, Dudulle.

    Les deux ont des partisans, et des raisons. Le principe d’un standard / d’une convention, c’est de fixer les choses. En l’occurrence, pour ce qui est des tabs et spaces, le risque qui a voulu être évité, de mémoire, c’est l’indentation mixte. La valeur équivalente en spaces d’un tab est variable de système en système, et même d’un IDE à l’autre. Dès que tu bosses avec autrui sur un code, tu prends le risque de te retrouver avec des tabs dans tes spaces ou des spaces dans tes tabs, qui visuellement vont avoir la bonne gueule chez lui, et c’est la mouise à gérer. Du coup, l’interpréteur ne gère pas l’indentation mixte, parce qu’il ne peut pas. Du coup il a fallu choisir. Du coup, comme de toutes façons un espace fait toujours la taille d’un espace c’est celui là qui a été choisi, de sorte que quand je jette un oeil sur le code pourri qui bugge d’un collègue l’allure du code est aussi lisible que chez moi.

    Après, il y a des guides de style qui vont “amender” la PEP8, en particulier sur la limite mégachiante de 79 (c’est pas 80, Sam, c’est 79, parce que tant qu’à être absurde…) caractères par ligne. Google par exemple fait du python PEP8 sauf indentation à deux espaces et lignes de 120 caractères max.

    La PEP8 est avant tout un guide, c’est pas une police, comme Raymond Hettinger le dit bien (lien). Il se trouve que si les gens se sont gratté la tête pour chaque choix (et sur python-ideas, ça cogite) c’est parce que ça améliore la lisibilité sur le long terme, sur le gros code, et d’une machine à l’autre. Accessoirement, respecter la PEP8 (même sur les tab, dans une certaine mesure) aide à intégrer le Zen of Python : indenter c’set un poil plus chiant pour toi ? Ça tombe bien, “Flat is better than nested” ;)

  • Dudulle

    N’ayant certainement pas assez participé à des projets collaboratifs en Python, je n’avais effectivement pas pris en compte le temps perdu par de multiple échanges et donc adaptation de petits bouts de code suivant une norme au milieu d’un autre code en suivant une autre.

    Vous avez illuminé ma journée, gloire à vous. (Si la gloire ne suffit pas, je paie ma bière.)

  • nicnic

    salut sam & max, merci pour ce que vous faites.

    De toute façon en général il y a moyen de faire en sorte dans un éditeur de texte qu’appuyer sur “tab” tape “4 espaces” ? Par exemple dans sublime text il me semble que c’est ce qu’il se passe par défaut. Dans ce cas en pratique on s’en fout et ça revient au même à l’écriture non ?

Leave a comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.