UKOnline

Convention de codage

Terminons ce livre en beauté en passant en revue différentes conventions globalement acceptées, qui vous permettront d'écrire du code pythonique. Pour rappel, les deux principaux mots clés souvent associés au Python sont lisibilité et concision.

Suivre des conventions apporte plusieurs avantages aux développeurs. Il est tout d'abord plus facile de rentrer dans le code écrit par un autre développeur que soi. Cela permet également d'obtenir un code plus uniforme pour un projet qui est écrit par plusieurs développeurs. La productivité d'une équipe peut ainsi être accrue, grâce aux gains de temps obtenus par une meilleure lisibilité du code et grâce à la consistance accrue au sein du projet.

PEP 0008

On a déjà eu l'occasion de découvrir une partie des règles énoncées dans le PEP 0008 au chapitre 2. Voyons maintenant les règles qu'on n'avait pas encore pu voir précédemment car elles concernaient de la matière abordée entre temps :

  • un code source Python devrait être enregistré en UTF-8 ;
  • concernant les espaces :
    • il faut utiliser quatre espaces pour un niveau d'indentation, et on évite la tabulation horizontale ;
    • lorsqu'on a une longue liste de valeurs entre parenthèses (paramètres d'une fonction/méthode, condition d'un if/while), on peut les séparer sur plusieurs lignes en alignant le tout verticalement :
      def createperson(firstname, lastname, birthdate,
                       sex, address, weight, height):
          if (sex == 'F' or
              sex == 'M'):
              # ...
    • lorsqu'on a une longue liste de valeurs, entre parenthèses, crochets ou accolades, on peut les séparer sur plusieurs lignes en indentant le contenu :
      data = [
          1, 2, 3,
          4, 5, 6
      ]
    • lorsqu'on sépare une expression sur plusieurs lignes, on placera les opérateurs en début de chaque nouvelle ligne :
      total = (price
               + tax
               - sale)
    • on insère deux lignes vides avant et après une définition de classe ou de fonction, et une ligne vide avant et après une définition de méthode dans une classe ;
    • on ne met pas d'espace à l'intérieur de parenthèses, crochets ou accolades, avant un deux-points, autour d'un deux-points dans un slice, avant la parenthèse ouvrante d'un appel de fonction/méthode, avant un crochet pour accéder à un élément d'une liste ou d'un dictionnaire :
      # Pas bien
      def func(name = 'Jon'):
          preferences = { name    : ['Seb'  , 'Alexis'  ] }
          values      = data   [2 :4]
          verdict     = getresult ('Tom')
      
      # Bien
      def func(name='Jon'):
          preferences = {name: ['Seb', 'Alexis']}
          values = data[2:4]
          verdict = getresult('Tom')
    • on ne met qu'une seule espace avant et après l'opérateur d'affectation :
      # Pas bien
      data=32
      result     =   7
      
      # Bien
      data = 32
      result = 7
  • une ligne trop longue, notamment si elle dépasse $79$ caractères, peut être coupée avec le caractère de continuation (\) :
    with open('/Users/johndoe/Desktop/video-ole-ole.mp4'), \
         open('/Users/johndoe/Desktop/marchand-brouette.jpg'):
        # [...]
  • concernant les imports :
    • on écrit une ligne pour chaque module importé avec import, mais on peut les rassembler lorsqu'on utilise from ... import :
      # Pas bien
      from math import cos
      from math import sin
      import os, sys
      
      # Bien
      from math import cos, sin
      import os
      import sys
    • on organise les imports en trois blocs : d'abord les imports de la librairie standard, puis ceux de librairies tierces et enfin ceux des librairies développées pour l'application ;
    • il faut éviter, autant que possible, d'importer * ;
  • il n'y a aucune recommendation particulière pour délimiter les chaines de caractères, on peut utiliser librement des guillemets simples ou doubles, sauf pour les triples guillemets où on préfèrera les doubles pour respecter la convention de Docstring ;
  • on n'ajoute pas d'espaces au bout d'une ligne ;
  • on entoure toujours les opérateurs binaires suivants d'espaces : =, +=, -=..., ==, <, >, !=, <=, >=, in, not in, is, is not, and, or et not ;
  • on ne met pas d'espaces autour de l'opérateur = pour les paramètres nommés, lors de l'appel ou pour spécifier une valeur par défaut ;
  • concernant les noms :
    • ne pas utiliser les lettres l ($L$ minuscule), O ($O$ majuscule) et I ($I$ majuscule) dans un nom de variable, fonction, classe ;
    • un module ou un package doit avoir un nom court et rien qu'avec des minuscules ;
    • un nom de classe commence par une majuscule et suit la casse chameau (en casse chameau, appelée camelcase en anglais, chaque mot commence par une majuscule comme dans HotBeverageDistributor, par exemple) ;
    • un nom de fonction/méthode et de variable est en minuscule, en séparant éventuellement les mots avec des tirets de soulignement (_), si cela augmente la lisibilité ;
    • un nom de constante est complètement en majuscule, en séparant éventuellement les mots avec des tirets de soulignement ;
    from math import sqrt
    
    class ComplexNumber:
        def __init__(self, real, imag):
            self.__real = real
            self.__imag = imag
    
        def norm(self):
            return sqrt(self.__real ** 2 + self.__imag ** 2)
    
    ZERO = ComplexNumber(0, 0)

Code pythonique

Terminons avec quelques conventions de bon usage, adoptées par la grande majorité des programmeurs Python, qui font en sorte qu'un code est qualifié de pythonique, ou non. Voici, en vrac, une série d'éléments auxquels il convient de faire attention et qu'il faut exploiter pour rendre son code pythonique (pour en savoir plus, vous pouvez consulter les deux sites web suivants : http://docs.python-guide.org/en/latest/writing/style/ et http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html) :

  • pour intervertir les valeurs de deux variables a et b, on doit exploiter l'affectation multiple :
    # Pas bien
    temp = a
    a = b
    b = temp
    
    # Bien
    a, b = b, a
  • on doit exploiter le déballage lorsqu'on parcourt une structure de données qui contient des tuples ou des listes d'éléments. Par exemple, si on a une liste qui contient des contacts représentés par un prénom, un nom et un âge, on pourra écrire :
    data = [['John', 'Doe', 27], ['Tom', 'Doe', 45]]
    for (first, last, age) in data:
        print('{} {} ({} ans)'.format(first, last, age))
  • construire une chaine de caractères à partir de plusieurs valeurs est plus rapide avec la méthode join qu'avec une boucle de concaténations :
    data = ['hello', 'world!']
    
    # Pas bien
    result = ''
    for elem in data:
        result += str(elem) + ' '
    print(result[:-1])
    
    # Bien
    print(' '.join(data))
  • utiliser l'opérateur in lorsque c'est possible ;
  • utiliser la méthode get des dictionnaires, qui permet d'obtenir la valeur associée à une clé ou une autre valeur que l'on spécifie si la clé n'est pas dans le dictionnaire :
    stock = {}
    
    # Pas bien
    for (item, quantity) in data:
        if item not in stock:
            stock[item] = 0
        stock[item] += quantity
    
    # Bien
    for (item, quantity) in data:
        stock[item] = stock.get(item, 0) + quantity
  • une séquence vide valant False dans une condition, on ne doit pas utiliser len pour tester qu'elle est non vide :
    # Pas bien
    if len(data) != 0:
        print(data[0])
    
    # Bien
    if data:
        print(data[0])
  • exploiter au maximum la définition de liste par compréhension pour remplacer une construction faite par une boucle dans laquelle on utilise une condition. Par exemple, pour filtrer une liste et n'en garder que les valeurs paires, on peut faire :
    data = [8, -1, 15, 7, 9, 12]
    
    # Pas bien
    even = []
    for elem in data:
        if elem % 2 == 0:
            even.append(elem)
    
    # Bien
    even = [x for x in data if x % 2 == 0]

Enfin, si vous avez oublié la philosophie qui est derrière Python, si vous avez l'impression d'être sur le mauvais chemin et de ne plus être assez pythonique, lancez Python en mode interactif et importez le module this pour quelques rappels :

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!