In [ ]:
from __future__ import print_function

Contrôles d'exécution

A part l’instruction while que l’on vient de découvrir, Python comprend les instructions de contrôle d’exécution habituelles connues dans d’autres langages, avec quelques adaptations.

Instruction if

Peut-être l’instruction la plus connue est-elle l’instruction if. Par exemple :


In [6]:
x = int(input("Entrez un entier : "))
if x < 0:
    x = 0
    print ('Négatif changé en zéro')
elif x == 0: 
    print ('Zéro')
elif x == 1: print ('Un seul')
else: print ('Plus')


Entrez un entier : 0
Zéro

In [10]:
x="a"
x.isalpha()


Out[10]:
True

Il peut y avoir aucune ou plusieurs sections elif, et la section else est optionnelle. Le mot-clé elif est une abréviation de else if, et est utile pour éviter une indentation excessive. Une séquence if ... elif ... elif ... est un substitut pour les instructions switch ou case qu’on trouve dans d’autres langages.

Instruction for

L’instruction for en Python diffère un petit peu de ce que vous avez pu utiliser en C ou en Pascal. Au lieu d’itérer toujours dans une progression arithmétique de nombres (comme en Pascal), ou de laisser l’utilisateur complètement libre dans les tests et les pas d’itération (comme en C), l’instruction for de Python itère parmi les éléments de n’importe quelle séquence (une liste ou une chaîne), dans l’ordre où ils apparaissent dans la séquence. Par exemple :


In [11]:
# Mesurer quelques chaînes de caractères:
liste = ['plat', 'végane', 'manger']
for element in liste:
    print (element, len(element), end=', ')


plat 4, végane 6, manger 6, 

Il n’est pas prudent de modifier la séquence sur laquelle on itère dans la boucle (cela peut seulement arriver pour les types de séquences modifiables, tels que les listes). Si vous avez besoin de modifier la liste sur laquelle vous itérez (par exemple, pour dupliquer des éléments sélectionnés), vous devez itérer sur une copie. La notation de découpage rend cela particulièrement pratique :


In [ ]:
for x in liste[:]: # fait une copie de la liste entière par découpage
    if len(x) > 8: liste.insert(0, x)
        
print (liste)

La fonction range

Si vous avez besoin d’itérer sur une séquence de nombres, la fonction intégrée range() vient à point. Elle génère des listes contenant des progressions arithmétiques :


In [12]:
range(10) # équivalent à [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


Out[12]:
range(0, 10)

Le nombre de fin qui lui est passé n’est jamais dans la liste générée ; range(10) génère une liste de 10 valeurs, exactement les indices des éléments d’une séquence de longueur 10. Il est possible de faire commencer l’intervalle à un autre nombre, ou de spécifier un incrément différent (même négatif) :


In [8]:
range(5, 10) # [5, 6, 7, 8, 9]


Out[8]:
range(5, 10)

In [16]:
range(0, 10, 2) # [0, 2, 4, 6, 8]


Out[16]:
range(0, 10, 2)

In [ ]:
range(10, -20, -5) # [10, 5, 0, -5, -10, -15]

Habituellement, on combine for et range de la façon suivante :


In [13]:
liste = ['plat', 'végane', 'manger']
for i in range(0, 3, 1):  # comme for (i=0; i<3; i++)
    print (liste[i], "est l'élément d'indice", i)


plat est l'élément d'indice 0
végane est l'élément d'indice 1
manger est l'élément d'indice 2

Pour parcourir les indices d’une séquence, combinez range() et len() comme ci-dessous :


In [14]:
fable = ['Le', 'Lion', 'et', 'le', 'Moucheron']
for i in range(len(fable)):
    print ("L'élément d'indice", i, 'est', fable[i])


L'élément d'indice 0 est Le
L'élément d'indice 1 est Lion
L'élément d'indice 2 est et
L'élément d'indice 3 est le
L'élément d'indice 4 est Moucheron

Les instructions break et continue, et les clauses else dans les boucles (*)

L’instruction break, comme en C, sort de la plus petite boucle for ou while englobante.

L’instruction continue, également empruntée au C, continue sur la prochaine itération de la boucle.

Les instructions de boucle ont une clause else ; elle est exécutée lorsque la boucle se termine par épuisement de la liste (avec for) ou quand la condition devient fausse (avec while), mais pas quand la boucle est interrompue par une instruction break. Cela est expliqué dans la boucle suivante, qui recherche des nombres premiers :


In [11]:
for n in range(2, 10):
    for x in range(2, n):
        if n % x == 0:
            print (n, 'égale', x, '*', n//x) # // est la division entière
            break
    else:
        # la boucle s'est terminée sans trouver facteur
        print (n, 'est un nombre premier')


2 est un nombre premier
3 est un nombre premier
4 égale 2 * 2
5 est un nombre premier
6 égale 2 * 3
7 est un nombre premier
8 égale 2 * 4
9 égale 3 * 3

L'instruction pass (*)

L’instruction pass ne fait rien. Elle peut être utilisée lorsqu’une instruction est requise syntaxiquement mais que le programme ne nécessite aucune action. Par exemple :


In [ ]:
n =  -10
if n > 0:
    pass
else:
    print ('Attention n est négatif')

Définition de fonctions

Nous pouvons créer une fonction qui écrit la série de Fibonacci jusqu’à une limite quelconque :


In [16]:
def fib(n):    # écrit la série de Fibonacci jusqu’à n
    """Affiche une suite de Fibonacci jusqu'à n."""
    a, b = 0, 1
    while b < n:
        print (b, end=' ')
        a, b = b, a+b
        
# Maintenant on appelle la fonction qui vient juste d’être définie
fib(1000)


1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 

Si on veut tester que l'utilisateur rentre bien un nombre, nous pouvons écrire :


In [17]:
def fib(n):    # écrit la série de Fibonacci jusqu’à n
    """Affiche une suite de Fibonacci jusqu'à n."""
    if type(n) != int:
        print (n, "n'est pas un entier")
        return
    a, b = 0, 1
    while b < n:
        print (b, end=' ')
        a, b = b, a+b
    print()
        
# Maintenant on appelle la fonction qui vient juste d’être définie
fib(1000000)
fib('toto')


1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 
toto n'est pas un entier

Le mot-clé def débute la définition d’une fonction. Il doit être suivi par le nom de la fonction et une liste entre parenthèses de paramètres formels. Les instructions qui forment le corps de la fonction commencent sur la ligne suivante, indentée par une tabulation. La première instruction du corps de la fonction peut éventuellement être un texte dans une chaîne de caractères ; cette chaîne est la chaîne de documentation de la fonction, ou docstring.

Il y a des outils qui utilisent les docstrings pour générer automatiquement de la documentation papier, ou pour permettre à l’utilisateur de naviguer interactivement dans le code ; c’est une bonne technique que d’inclure les docstrings dans le code que vous écrivez, donc essayez de vous y habituer.


In [18]:
fib?

L’exécution d’une fonction génère une nouvelle table de symboles, utilisée pour les variables locales de la fonction. Plus précisément, toutes les affectations de variables dans une fonction stockent la valeur dans la table de symboles locale ; alors que les références à des variables regardent en premier dans la table de symboles locale, puis dans la table de symboles globale, et enfin dans la table des noms intégrés. Ainsi, on ne peut affecter directement une valeur aux variables globales à l’intérieur d’une fonction (à moins de les déclarer avec une instruction global), bien qu’on puisse y faire référence.

Les vrais paramètres (arguments) d’un appel de fonction sont introduits dans la table de symboles locale de la fonction appelée quand elle est appelée ; ainsi, les arguments sont passés en utilisant un passage par valeur. Quand une fonction appelée appelle à son tour une autre fonction, une nouvelle table de symboles locaux est créée pour cet appel. La définition d’une fonction introduit le nom de la fonction dans la table de symboles courante. La valeur du nom de la fonction a un type qui est reconnu par l’interpréteur comme une fonction définie par l’utilisateur. Cette valeur peut être affectée à un autre nom qui peut alors être utilisé aussi comme une fonction. Cela permet de disposer d’un mécanisme général de renommage :


In [14]:
type(fib)


Out[14]:
function

In [15]:
f = fib
f(100)


1 1 2 3 5 8 13 21 34 55 89 

Nous pourrions noter que fib n’est pas une fonction mais une procédure. En Python, comme en C, les procédures sont juste des fonctions qui ne retournent pas de valeur. En fait, techniquement parlant, les procédures retournent bien une valeur, bien qu’elle soit plutôt décevante. Cette valeur est appelée None (c’est un nom intégré). La valeur None n’est normalement pas affichée par l’interpréteur si elle devait être la seule valeur écrite. Vous pouvez le vérifier si vous y tenez vraiment :


In [ ]:
print (fib(0))

Écrire une fonction qui retourne une liste des nombres de la suite de Fibonacci, au lieu de les imprimer, est très simple :


In [20]:
def fib2(n): # retourne la série de Fibonacci jusqu’à n
    """Retourne une liste contenant la série de Fibonacci jusqu'à n."""
    resultat = list()
    a, b = 0, 1
    while b < n:
        resultat.append(b) # voir ci-dessous
        a, b = b, a+b
    return resultat
        
f100 = fib2(100)  # Appel de la fonction
print (f100)      # Affichage du résultat
print (f100[-1]) # le dernier élément de la série avant 100


[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
89

Cet exemple, comme d’habitude, démontre quelques nouvelles caractéristiques de Python :

  • L’instruction return termine une fonction en renvoyant une valeur. return sans une expression en argument renvoie None. Parvenir jusqu’au bout de la procédure renvoie également None.
  • L’instruction resultat.append(b) appelle une méthode de l’objet resultat. Une méthode est une fonction qui "appartient" à un objet et est nommée obj.nommethode, où obj est un objet (cela pourrait être une expression), et nommethode est le nom d’une méthode qui est définie d’après le type de l’objet. Différents types définissent différentes méthodes. Les méthodes de types différents peuvent avoir le même nom sans que cela soit source d’ambiguïtés. (Il est possible de définir vos propres types d’objets et méthodes, en utilisant des classes, mais cela sort du cadre de ce cours.) La méthode append() montrée précédemment, est définie pour les objets listes ; elle ajoute un nouvel élément à la fin de la liste. Dans cet exemple, c’est équivalent à resultat = resultat + [b], mais en plus performant.

Encore plus sur la définition de fonctions

Il est aussi possible de définir des fonctions à nombre d’arguments variable. Il y a trois façons de faire, qui peuvent être combinées.

Valeurs d’argument par défaut

La technique la plus utile consiste à spécifier une valeur par défaut pour un ou plusieurs arguments. Cela crée une fonction qui peut être appelée avec moins d’arguments qu’il n’en a été défini.


In [23]:
def demande_ok(question, tentatives=3, plainte='Oui ou non, svp!'):
    while 1:
        ok = input(question)
        if ok in ('o', 'ou', 'oui'): return 1
        if ok in ('n', 'no', 'non', 'niet'): return 0
        tentatives = tentatives - 1
        if tentatives <= 0: raise (IOError, 'utilisateur refuse')
        print (plainte)
        
# Cette fonction peut être appelée soit comme ceci :
demande_ok('Etes vous sûr de vouloir quitter ?')
# ou comme ceci
demande_ok('OK pour écrasement du fichier ?', 2, 'êtes-vous sûr?')


Etes vous sûr de vouloir quitter ?o
OK pour écrasement du fichier ?nok
êtes-vous sûr?
OK pour écrasement du fichier ?oui
Out[23]:
1

Les valeurs par défaut sont évaluées au moment de la définition de la fonction dans la portée de définition, ainsi:


In [ ]:
i=5
def f (arg = i):
    print (arg)
    
i = 6
f()

Avertissement important (**) : La valeur par défaut est évaluée seulement une fois. Cela est important lorsque la valeur par défaut est un objet modifiable comme une liste ou un dictionnaire. Par exemple, la fonction suivante accumule les arguments qui lui sont passés au fur et à mesure des appels :


In [ ]:
def f(a, L=[]):
    L.append(a)
    return L

print (f(1))
print (f(2))
print (f(3))

Si vous ne voulez pas que la valeur par défaut soit partagée entre des appels successifs, vous pouvez plutôt écrire la fonction comme ceci :


In [ ]:
def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L

Argument à mot-clé

Les fonctions peuvent aussi être appelées en utilisant des arguments mots-clés de la forme motcle = valeur. Par exemple, la fonction suivante :


In [ ]:
def mafonc(x, p=2, debug=False):
    """
    Calcule x à la puissance p. p vaut 2 par défaut.
    """
    if debug:
        print("evaluation de mafonc pour x = " + str(x) + 
              " en utilisant comme exposant p = " + str(p))
    return x**p

pourrait être appelée de l'une des façon suivante:


In [ ]:
print ('Le résultat est :', mafonc(5))

In [ ]:
mafonc(5, 3, False)

In [ ]:
mafonc(5, debug=True)

In [ ]:
mafonc(p=3, debug=True, x=7)

En général, une liste d’arguments doit être constituée de tous les arguments de position, suivis de tous les arguments mots-clés, où ces mots-clés doivent être choisis parmi les noms des paramètres formels. Il n’est pas important qu’un paramètre formel ait une valeur par défaut ou non. Aucun argument ne peut recevoir une valeur plus d’une fois - les noms de paramètre formel correspondant aux arguments de position ne peuvent être utilisés comme mots-clés dans les mêmes appels.


In [ ]:
def function(a):
    pass

function(0, a=0)

Quand un paramètre formel de la forme **nom est présent en dernière position, il reçoit un dictionnaire contenant tous les arguments mots-clés dont les mots-clés ne correspondent pas à un paramètre formel. Cela peut être combiné avec un paramètre formel de la forme *nom (décrit dans la sous-section suivante) qui reçoit un tuple contenant les arguments positionnels au-delà de la liste de paramètres formels. (*nom doit être placé avant **nom.) Par exemple, nous définissons une fonction comme ceci :


In [ ]:
def fromagerie(type, *arguments, **motcles):
         print ("-- Avez-vous du", type, '?')
         print ("-- Je suis désolé, plus personne n’a de", type)
         for arg in arguments: print (arg)
         print ('-'*40)
         cles = motcles.keys()
         cles.sort()
         for mc in cles : print (mc, ':', motcles[mc])

qui pourrait être appelée comme ceci:


In [ ]:
fromagerie('Camembert', "Il est très coulant, monsieur.",
                "Il est vraiment très, TRES coulant, monsieur.",
                client='John Cleese',
                proprietaire='Michael Palin',
                sketch='Sketch de la Fromagerie')

Notez que la méthode sort() de la liste de des mots-clés des noms d’arguments est appelée avant d’imprimer le contenu du dictionnaire motcles ; si cela n’est pas fait, l’ordre dans lequel les arguments sont imprimés n’est pas défini.

Listes d’arguments à déballer (*)

La situation inverse se produit lorsque les arguments sont dans une liste ou un n-uplet mais doivent être déballés en vue d’une fonction qui requiert des arguments positionnels séparés. Par exemple, la fonction intégrée range() attend deux arguments séparés start et stop. Si ces derniers ne sont pas disponibles séparément, écrivez l’appel de la fonction avec l’opérateur * afin de déballer les arguments depuis une liste ou un n-uplet :


In [ ]:
range(3, 6)  # appel normal avec des arguments séparés

In [ ]:
args = [3, 6]
range(*args) # appel avec des arguments déballés depuis une liste

Les formes lambda (*)

Avec le mot-clé lambda, de petites fonctions anonymes peuvent être créées. Voici une fonction qui retourne la somme de ses deux arguments: lambda a, b : a+b. Les formes Lambda peuvent être utilisées chaque fois qu’un objet fonction est requis. Elles sont limitées syntaxi- quement à une expression unique. Sémantiquement, elles sont juste de l’enrobage syntaxique pour une définition de fonction normale. Comme les définitions de fonctions imbriquées, les formes lambda peuvent faire référence à des variables de la portée qui les contient :


In [ ]:
def fabrique_incrementeur(n):
    return lambda x, incr=n: x+incr

f = fabrique_incrementeur(42)
f(0)

In [ ]:
f(1)

Chaînes de documentation (docstrings)

Il existe des conventions émergentes à propos du contenu et du formatage des chaînes de documentation.

La première ligne devrait toujours être un résumé concis des objectifs de l’objet. Afin d’être bref, il ne devrait pas répéter explicitement le nom ou le type de l’objet, puisque ces informations sont disponibles par d’autres moyens (sauf si le nom se trouve être un verbe décrivant l’utilisation d’une fonction). Cette ligne devrait toujours commencer par une lettre majuscule et finir par une virgule.

S’il y a d’autres lignes dans la chaîne de documentation, la deuxième ligne devrait être vide, séparant visuellement le résumé du reste de la description. Les lignes suivantes devraient constituer un ou plusieurs paragraphes décrivant les conventions d’appel des objets, ses effets de bord, etc.

L’interpréteur python ne supprime pas l’indentation des chaînes de texte multilignes en Python, donc les outils qui traitent la documentation doivent supprimer l’indentation. Cela peut se faire en utilisant la convention suivante. La première ligne non-vide après la première ligne de la chaîne détermine la quantité d’indentation pour toute la chaîne de documentation. (On ne peut pas utiliser la première ligne puisqu’elle est généralement adjacente aux quotes ouvrantes de la chaîne donc son indentation n’est pas apparente dans le texte de la chaîne.) Les espaces “équivalents” à cette indentation sont ensuite supprimés du début de toutes les lignes de la chaîne. Des lignes indentées de façon moins importante ne devraient pas apparaître, mais si elles le font, tous leurs espaces en début de ligne devraient être supprimés. L’équivalence de l’espacement devrait être testée après l’expansion des tabulations (à 8 espaces, normalement).

Voici un exemple de docstring multi-ligne :


In [ ]:
def ma_fonction():
    """Ne fait rien, mais le documente.
    
    Non, vraiment elle ne fait rien.
    """
    pass

print (ma_fonction.__doc__)
ma_fonction?
help(ma_fonction)

Ce notebook est une adaptation de la traduction française, dirigée par Olivier Berger et mise à jour par Henri Garreta, du tutoriel Python édité par Guido van Rossum et Fred L. Drake.