Sous-sections


4. D'Autres Outils de Contrôle 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.


4.1 Instructions if

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

>>> 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'
...

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.


4.2 Instructions 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 (par ex., une liste ou une chaîne), dans l'ordre où ils apparaissent dans la séquence. Par exemple (aucun jeu de mots volontaire):

>>> # Mesurer quelques chaînes:
... a = ['chat', 'fenêtre', 'défenestrer']
>>> for x in a:
...     print x, len(x)
... 
chat 4
fenêtre 7
défenestrer 11

Il n'est pas prudent de modifier la séquence sur laquelle on itère dans la boucle (ceci peut seulement arriver pour les types de séquences modifiables, par exemple, 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 ceci particulièrement pratique:

>>> for x in a[:]: # fait une copie de la liste entière par découpage
...    if len(x) > 8: a.insert(0, x)
... 
>>> a
['défenestrer', 'chat', 'fenêtre', 'défenestrer']


4.3 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, par ex.:

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

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):

>>> range(5, 10)
[5, 6, 7, 8, 9]
>>> range(0, 10, 3)
[0, 3, 6, 9]
>>> range(-10, -100, -30)
[-10, -40, -70]

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

>>> a = ['Marie', 'avait', 'un', 'petit', 'mouton']
>>> for i in range(len(a)):
...     print i, a[i]
... 
0 Marie
1 avait
2 un
3 petit
4 mouton


4.4 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. Ceci est expliqué dans la boucle suivante, qui recherche des nombres premiers:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...            print n, 'égale', x, '*', n/x
...            break
...     else:
...          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


4.5 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:

>>> while 1:
...       pass # Attente active d'une interruption au clavier
...


4.6 Définition de Fonctions

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

>>> 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,
...         a, b = b, a+b
... 
>>> # Maintenant on appelle la fonction qui vient juste d'être définie
... fib(2000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

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.

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.4.1Quand 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. Ceci permet de disposer d'un mécanisme général de renommage:

>>> fib
<function object at 10042ed0>
>>> f = fib
>>> f(100)
1 1 2 3 5 8 13 21 34 55 89

Vous pourriez objecter 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:

>>> print fib(0)
None

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

>>> def fib2(n): # retourne la série de Fibonacci jusqu'à n
...     "Retourne une liste contenant la série de Fibonacci jusqu'à n"
...     resultat = []
...     a, b = 0, 1
...     while b < n:
...         resultat.append(b)    # voir ci-dessous
...         a, b = b, a+b
...     return resultat
... 
>>> f100 = fib2(100)    # on l'appelle
>>> f100                # écrire le résultat
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 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 est utilisé pour retourner depuis le milieu d'une procédure (aboutir à la fin termine aussi la procédure), auquel cas la valeur None est retournée.

  • L'instruction result.append(b) appelle une méthode de l'objet result. 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, de la façon décrite en 9.) 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 à "result = result + [b]", mais en plus performant.


4.7 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.


4.7.1 Valeurs d'Argument par Défaut

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

def demande_ok(question, tentatives=4, plainte='Oui ou non, svp!'):
    while 1:
        ok = raw_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 refusenik'
        print plainte

Cette fonction peut être appelée soit comme ceci: demande_ok('Etes vous sûr de vouloir quitter?')4.2 ou comme ceci: demande_ok('OK pour écrasement du fichier?', 2).

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 par ex.

i = 5
def f(arg = i): print arg
i = 6
f()

affichera 5.

Avertissement important: La valeur par défaut est évaluée seulement une fois. Ceci 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:

def f(a, l = []):
    l.append(a)
    return l
print f(1)
print f(2)
print f(3)

Ceci affichera

[1]
[1, 2]
[1, 2, 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:

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


4.7.2 Arguments à 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:

def perroquet(voltage, etat='c\'est du solide', action='voom', type='Bleu Norvégien'):
    print "-- Ce perroquet ne fera pas", action,
    print "si vous le mettez sous", voltage, "Volts."
    print "-- Beau plumage, le", type
    print "-- Ca", etat, "!"

pourrait être appelée de l'une des façons suivantes:

perroquet(1000)
perroquet(action = 'VOOOOOM', voltage = 1000000)
perroquet('un millier', etat = 'fait bouffer les pissenlits par la racine')
perroquet('un milion', 'vous dégoute de la vie', 'de bonds')

mais les appels suivants seraient tous invalides:

perroquet()                          # manque un argument obligatoire
perroquet(voltage=5.0, 'rend mort')  # un argument non-mot-clé suit un mot-clé
perroquet(110, voltage=220)          # doublon de valeurs pour un argument
perroquet(acteur='John Cleese')      # mot-clé inconnu

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.

>>> def function(a):
...     pass
... 
>>> function(0, a=0)
Traceback (innermost last):
  File "<stdin>", line 1, in ?
TypeError: keyword parameter redefined

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. Ceci 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:

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
    for mc in motcles.keys(): print mc, ':', motcles[mc]

Elle pourrait être appelée comme ceci:

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' )

et bien sûr, elle écrirait:

-- Avez-vous du Camembert ?
-- Je suis désolé, plus personne n'a de 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


4.7.3 Listes d'Arguments Arbitraires

Finalement, l'option la moins fréquemment utilisée est de spécifier qu'une fonction peut être appelée avec un nombre d'arguments arbitraire. Ces arguments seront récupérés dans un tuple. Avant le nombre variable d'arguments, zéro ou plus arguments normaux pourraient être présents.

def fprintf(fichier, format, *args):
    fichier.write(format % args)


4.7.4 Les Formes Lambda

Suite à la demande populaire, quelques caractéristiques trouvées habituellement dans les langages de programmation fonctionnelle et dans Lisp ont été ajoutées à Python. 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 syntaxiquement à 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 ne peuvent pas faire référence à des variables de la portée qui les contient, mais ceci peut être surpassé en utilisant judicieusement les valeurs des arguments par défaut, par ex.

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


4.7.5 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. Ceci 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:

>>> def ma_fonction():
...     """Ne fait rien, mais le documente.
... 
...     Non, vraiment, elle ne fait rien.
...     """
...     pass
... 
>>> print ma_fonction.__doc__
Ne fait rien, mais le documente.

    Non, vraiment, elle ne fait rien.



Notes

... valeur.4.1
En réalité, passage par référence d'objet serait une meilleure description, puisque si un objet modifiable est passé, l'appelant verra tous les changements que l'appelé y effectue (par ex., des éléments insérés dans une liste).
... quitter?')4.2
NDT : l'accent circomflexe sur le ``u'' de ``sur'' s'affiche assez mal chez nous. Désolé, mais notre compétence en LaTeX est limitée. Merci de nous signaler le correctif.

See About this document... for information on suggesting changes.