Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Ce cours existe en eBook.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

J'ai tout compris !
Apprenez à programmer en Python

Apprenez à programmer en Python

Mis à jour le lundi 18 août 2014
  • 4 semaines
  • Facile

En programmation, on est souvent amené à utiliser plusieurs fois des groupes d'instructions dans un but très précis. Attention, je ne parle pas ici de boucles. Simplement, vous pourrez vous rendre compte que la plupart de nos tests pourront être regroupés dans des blocs plus vastes, fonctions ou modules. Je vais détailler tranquillement ces deux concepts.

Les fonctions permettent de regrouper plusieurs instructions dans un bloc qui sera appelé grâce à un nom. D'ailleurs, vous avez déjà vu des fonctions : print et input en font partie par exemple.

Les modules permettent de regrouper plusieurs fonctions selon le même principe. Toutes les fonctions mathématiques, par exemple, peuvent être placées dans un module dédié aux mathématiques.

Les fonctions : à vous de jouer

Nous avons utilisé pas mal de fonctions depuis le début de ce tutoriel. On citera pour mémoire print, type et input, sans compter quelques autres. Mais vous devez bien vous rendre compte qu'il existe un nombre incalculable de fonctions déjà construites en Python. Toutefois, vous vous apercevrez aussi que, très souvent, un programmeur crée ses propres fonctions. C'est le premier pas que vous ferez, dans ce chapitre, vers la modularité. Ce terme un peu barbare signifie que nous allons nous habituer à regrouper dans des fonctions des parties de notre code que nous serons amenés à réutiliser. Au prochain chapitre, nous apprendrons à regrouper nos fonctions ayant un rapport entre elles dans un fichier, pour constituer un module, mais n'anticipons pas.

La création de fonctions

Nous allons, pour illustrer cet exemple, reprendre le code de la table de multiplication, que nous avons vu au chapitre précédent et qui, décidément, n'en finit pas de vous poursuivre.

Nous allons emprisonner notre code calculant la table de multiplication par 7 dans une fonction que nous appellerons table_par_7.

On crée une fonction selon le schéma suivant :

def nom_de_la_fonction(parametre1, parametre2, parametre3, parametreN):
    # Bloc d'instructions

Les blocs d'instructions nous courent après aussi, quel enfer. Si l'on décortique la ligne de définition de la fonction, on trouve dans l'ordre :

  • def, mot-clé qui est l'abréviation de « define » (définir, en anglais) et qui constitue le prélude à toute construction de fonction.

  • Le nom de la fonction, qui se nomme exactement comme une variable (nous verrons par la suite que ce n'est pas par hasard). N'utilisez pas un nom de variable déjà instanciée pour nommer une fonction.

  • La liste des paramètres qui seront fournis lors d'un appel à la fonction. Les paramètres sont séparés par des virgules et la liste est encadrée par des parenthèses ouvrante et fermante (là encore, les espaces sont optionnels mais améliorent la lisibilité).

  • Les deux points, encore et toujours, qui clôturent la ligne.

Le code pour mettre notre table de multiplication par 7 dans une fonction serait donc :

def table_par_7():
    nb = 7
    i = 0 # Notre compteur ! L'auriez-vous oublié ?
    while i < 10: # Tant que i est strictement inférieure à 10,
        print(i + 1, "*", nb, "=", (i + 1) * nb)
        i += 1 # On incrémente i de 1 à chaque tour de boucle.

Quand vous exécutez ce code à l'écran, il ne se passe rien. Une fois que vous avez retrouvé les trois chevrons, essayez d'appeler la fonction :

>>> table_par_7()
1 * 7 = 7
2 * 7 = 14
3 * 7 = 21
4 * 7 = 28
5 * 7 = 35
6 * 7 = 42
7 * 7 = 49
8 * 7 = 56
9 * 7 = 63
10 * 7 = 70
>>>

Bien, c'est, euh, exactement ce qu'on avait réussi à faire au chapitre précédent et l'intérêt ne saute pas encore aux yeux. L'avantage est que l'on peut appeler facilement la fonction et réafficher toute la table sans avoir besoin de tout réécrire !

Mais, si on saisit des paramètres pour pouvoir afficher la table de 5 ou de 8… ?

Oui, ce serait déjà bien plus utile. Je ne pense pas que vous ayez trop de mal à trouver le code de la fonction :

def table(nb):
    i = 0
    while i < 10: # Tant que i est strictement inférieure à 10,
        print(i + 1, "*", nb, "=", (i + 1) * nb)
        i += 1 # On incrémente i de 1 à chaque tour de boucle.

Et là, vous pouvez passer en argument différents nombres, table(8) pour afficher la table de multiplication par 8 par exemple.

On peut aussi envisager de passer en paramètre le nombre de valeurs à afficher dans la table.

def table(nb, max):
    i = 0
    while i < max: # Tant que i est strictement inférieure à la variable max,
        print(i + 1, "*", nb, "=", (i + 1) * nb)
        i += 1

Si vous tapez à présent table(11, 20), l'interpréteur vous affichera la table de 11, de 1*11 à 20*11. Magique non ?

Si vous fournissez en second paramètre un nombre négatif, vous avez toutes les chances de créer une magnifique boucle infinie… vous pouvez l'empêcher en rajoutant des vérifications avant la boucle : par exemple, si le nombre est négatif ou nul, je le mets à 10. En Python, on préférera mettre un commentaire en tête de fonction ou une docstring, comme on le verra ultérieurement, pour indiquer que max doit être positif, plutôt que de faire des vérifications qui au final feront perdre du temps. Une des phrases reflétant la philosophie du langage et qui peut s'appliquer à ce type de situation est « we're all consenting adults here » (en français, « Nous sommes entre adultes consentants »). Sous-entendu, quelques avertissements en commentaires sont plus efficaces qu'une restriction au niveau du code. On aura l'occasion de retrouver cette phrase plus loin, surtout quand on parlera des objets.

Valeurs par défaut des paramètres

On peut également préciser une valeur par défaut pour les paramètres de la fonction. Vous pouvez par exemple indiquer que le nombre maximum d'affichages doit être de 10 par défaut (c'est-à-dire si l'utilisateur de votre fonction ne le précise pas). Cela se fait le plus simplement du monde :

def table(nb, max=10):
    """Fonction affichant la table de multiplication par nb
    de 1*nb à max*nb
    
    (max >= 0)"""
    i = 0
    while i < max:
        print(i + 1, "*", nb, "=", (i + 1) * nb)
        i += 1

Il suffit de rajouter =10 après max. À présent, vous pouvez appeler la fonction de deux façons : soit en précisant le numéro de la table et le nombre maximum d'affichages, soit en ne précisant que le numéro de la table (table(7)). Dans ce dernier cas, max vaudra 10 par défaut.

J'en ai profité pour ajouter quelques lignes d'explications que vous aurez sans doute remarquées. Nous avons placé une chaîne de caractères, sans la capturer dans une variable, juste en-dessous de la définition de la fonction. Cette chaîne est ce qu'on appelle une docstring que l'on pourrait traduire par une chaîne d'aide. Si vous tapez help(table), c'est ce message que vous verrez apparaître. Documenter vos fonctions est également une bonne habitude à prendre. Comme vous le voyez, on indente cette chaîne et on la met entre triple guillemets. Si la chaîne figure sur une seule ligne, on pourra mettre les trois guillemets fermants sur la même ligne ; sinon, on préférera sauter une ligne avant de fermer cette chaîne, pour des raisons de lisibilité. Tout le texte d'aide est indenté au même niveau que le code de la fonction.

Enfin, sachez que l'on peut appeler des paramètres par leur nom. Cela est utile pour une fonction comptant un certain nombre de paramètres qui ont tous une valeur par défaut. Vous pouvez aussi utiliser cette méthode sur une fonction sans paramètre par défaut, mais c'est moins courant.

Prenons un exemple de définition de fonction :

def fonc(a=1, b=2, c=3, d=4, e=5):
    print("a =", a, "b =", b, "c =", c, "d =", d, "e =", e)

Simple, n'est-ce pas ? Eh bien, vous avez de nombreuses façons d'appeler cette fonction. En voici quelques exemples :

Instruction

Résultat

fonc()

a = 1 b = 2 c = 3 d = 4 e = 5

fonc(4)

a = 4 b = 2 c = 3 d = 4 e = 5

fonc(b=8, d=5)

a = 1 b = 8 c = 3 d = 5 e = 5

fonc(b=35, c=48, a=4, e=9)

a = 4 b = 35 c = 48 d = 4 e = 9

Je ne pense pas que des explications supplémentaires s'imposent. Si vous voulez changer la valeur d'un paramètre, vous tapez son nom, suivi d'un signe égal puis d'une valeur (qui peut être une variable bien entendu). Peu importent les paramètres que vous précisez (comme vous le voyez dans cet exemple où tous les paramètres ont une valeur par défaut, vous pouvez appeler la fonction sans paramètre), peu importe l'ordre d'appel des paramètres.

Signature d'une fonction

On entend par « signature de fonction » les éléments qui permettent au langage d'identifier ladite fonction. En C++, par exemple, la signature d'une fonction est constituée de son nom et du type de chacun de ses paramètres. Cela veut dire que l'on peut trouver plusieurs fonctions portant le même nom mais dont les paramètres diffèrent. Au moment de l'appel de fonction, le compilateur recherche la fonction qui s'applique à cette signature.

En Python comme vous avez pu le voir, on ne précise pas les types des paramètres. Dans ce langage, la signature d'une fonction est tout simplement son nom. Cela signifie que vous ne pouvez définir deux fonctions du même nom (si vous le faites, l'ancienne définition est écrasée par la nouvelle).

def exemple():
    print("Un exemple d'une fonction sans paramètre")

exemple()

def exemple(): # On redéfinit la fonction exemple
    print("Un autre exemple de fonction sans paramètre")

exemple()

A la ligne 1 on définit la fonction exemple. On l'appelle une première fois à la ligne 4. On redéfinit à la ligne 6 la fonction exemple. L'ancienne définition est écrasée et l'ancienne fonction ne pourra plus être appelée.

Retenez simplement que, comme pour les variables, un nom de fonction ne renvoie que vers une fonction unique, on ne peut surcharger de fonctions en Python.

L'instruction return

Ce que nous avons fait était intéressant, mais nous n'avons pas encore fait le tour des possibilités de la fonction. Et d'ailleurs, même à la fin de ce chapitre, il nous restera quelques petites fonctionnalités à voir. Si vous vous souvenez bien, il existe des fonctions comme print qui ne renvoient rien (attention, « renvoyer » et « afficher » sont deux choses différentes) et des fonctions telles que input ou type qui renvoient une valeur. Vous pouvez capturer cette valeur en plaçant une variable devant (exemple variable2 = type(variable1)). En effet, les fonctions travaillent en général sur des données et renvoient le résultat obtenu, suite à un calcul par exemple.

Prenons un exemple simple : une fonction chargée de mettre au carré une valeur passée en argument. Je vous signale au passage que Python en est parfaitement capable sans avoir à coder une nouvelle fonction, mais c'est pour l'exemple.

def carre(valeur):
    return valeur * valeur

L'instruction return signifie qu'on va renvoyer la valeur, pour pouvoir la récupérer ensuite et la stocker dans une variable par exemple. Cette instruction arrête le déroulement de la fonction, le code situé après le return ne s'exécutera pas.

variable = carre(5)

La variable variable contiendra, après exécution de cette instruction, 5 au carré, c'est-à-dire 25.

Sachez que l'on peut renvoyer plusieurs valeurs que l'on sépare par des virgules, et que l'on peut les capturer dans des variables également séparées par des virgules, mais je m'attarderai plus loin sur cette particularité. Retenez simplement la définition d'une fonction, les paramètres, les valeurs par défaut, l'instruction return et ce sera déjà bien.

Les fonctions lambda

Nous venons de voir comment créer une fonction grâce au mot-clé def. Python nous propose un autre moyen de créer des fonctions, des fonctions extrêmement courtes car limitées à une seule instruction.

Pourquoi une autre façon de créer des fonctions ? La première suffit, non ?

Disons que ce n'est pas tout à fait la même chose, comme vous allez le voir. Les fonctions lambda sont en général utilisées dans un certain contexte, pour lequel définir une fonction à l'aide de def serait plus long et moins pratique.

Syntaxe

Avant tout, voyons la syntaxe d'une définition de fonction lambda. Nous allons utiliser le mot-clé lambda comme ceci : lambda arg1, arg2,… : instruction de retour.

Je pense qu'un exemple vous semblera plus clair. On veut créer une fonction qui prend un paramètre et renvoie ce paramètre au carré.

>>> lambda x: x * x
<function <lambda> at 0x00BA1B70>
>>>

D'abord, on a le mot-clé lambda suivi de la liste des arguments, séparés par des virgules. Ici, il n'y a qu'un seul argument, c'est x. Ensuite figure un nouveau signe deux points « : » et l'instruction de la lambda. C'est le résultat de l'instruction que vous placez ici qui sera renvoyé par la fonction. Dans notre exemple, on renvoie donc x * x.

Comment fait-on pour appeler notre lambda ?

On a bien créé une fonction lambda mais on ne dispose ici d'aucun moyen pour l'appeler. Vous pouvez tout simplement stocker votre fonction lambda nouvellement définie dans une variable, par une simple affectation :

>>> f = lambda x: x * x
>>> f(5)
25
>>> f(-18)
324
>>>

Un autre exemple : si vous voulez créer une fonction lambda prenant deux paramètres et renvoyant la somme de ces deux paramètres, la syntaxe sera la suivante :

lambda x, y: x + y

Utilisation

À notre niveau, les fonctions lambda sont plus une curiosité que véritablement utiles. Je vous les présente maintenant parce que le contexte s'y prête et que vous pourriez en rencontrer certaines sans comprendre ce que c'est.

Il vous faudra cependant attendre un peu pour que je vous montre une réelle application des lambda. En attendant, n'oubliez pas ce mot-clé et la syntaxe qui va avec… on passe à la suite !

À la découverte des modules

Jusqu'ici, nous avons travaillé avec les fonctions de Python chargées au lancement de l'interpréteur. Il y en a déjà un certain nombre et nous pourrions continuer et finir cette première partie sans utiliser de module Python… ou presque. Mais il faut bien qu'à un moment, je vous montre cette possibilité des plus intéressantes !

Les modules, qu'est-ce que c'est ?

Un module est grossièrement un bout de code que l'on a enfermé dans un fichier. On emprisonne ainsi des fonctions et des variables ayant toutes un rapport entre elles. Ainsi, si l'on veut travailler avec les fonctionnalités prévues par le module (celles qui ont été enfermées dans le module), il n'y a qu'à importer le module et utiliser ensuite toutes les fonctions et variables prévues.

Il existe un grand nombre de modules disponibles avec Python sans qu'il soit nécessaire d'installer des bibliothèques supplémentaires. Pour cette partie, nous prendrons l'exemple du module math qui contient, comme son nom l'indique, des fonctions mathématiques. Inutile de vous inquiéter, nous n'allons pas nous attarder sur le module lui-même pour coder une calculatrice scientifique, nous verrons surtout les différentes méthodes d'importation.

La méthode import

Lorsque vous ouvrez l'interpréteur Python, les fonctionnalités du module math ne sont pas incluses. Il s'agit en effet d'un module, il vous appartient de l'importer si vous vous dites « tiens, mon programme risque d'avoir besoin de fonctions mathématiques ». Nous allons voir une première syntaxe d'importation.

>>> import math
>>>

La syntaxe est facile à retenir : le mot-clé import, qui signifie « importer » en anglais, suivi du nom du module, ici math.

Après l'exécution de cette instruction, rien ne se passe… en apparence. En réalité, Python vient d'importer le module math. Toutes les fonctions mathématiques contenues dans ce module sont maintenant accessibles. Pour appeler une fonction du module, il faut taper le nom du module suivi d'un point « . » puis du nom de la fonction. C'est la même syntaxe pour appeler des variables du module. Voyons un exemple :

>>> math.sqrt(16)
4
>>>

Comme vous le voyez, la fonction sqrt du module math renvoie la racine carrée du nombre passé en paramètre.

Mais comment suis-je censé savoir quelles fonctions existent et ce que fait math.sqrt dans ce cas précis ?

J'aurais dû vous montrer cette fonction bien plus tôt car, oui, c'est une fonction qui va nous donner la solution. Il s'agit de help, qui prend en argument la fonction ou le module sur lequel vous demandez de l'aide. L'aide est fournie en anglais mais c'est de l'anglais technique, c'est-à-dire une forme de l'anglais que vous devrez maîtriser pour programmer, si ce n'est pas déjà le cas. Une grande majorité de la documentation est en anglais, bien que vous puissiez maintenant en trouver une bonne part en français.

>>> help("math")
Help on built-in module math:

NAME
    math

FILE
    (built-in)

DESCRIPTION
    This module is always available. It provides access to the
    mathematical functions defined by the C standard.

FUNCTIONS
    acos(...)
        acos(x)

        Return the arc cosine (measured in radians) of x.

    acosh(...)
        acosh(x)

        Return the hyperbolic arc cosine (measured in radians) of x.

    asin(...)
-- Suite  --

Si vous parlez un minimum l'anglais, vous avez accès à une description exhaustive des fonctions du module math. Vous voyez en haut de la page le nom du module, le fichier qui l'héberge, puis la description du module. Ensuite se trouve une liste des fonctions, chacune étant accompagnée d'une courte description.

Tapez Q pour revenir à la fenêtre d'interpréteur, Espace pour avancer d'une page, Entrée pour avancer d'une ligne. Vous pouvez également passer un nom de fonction en paramètre de la fonction help.

>>> help("math.sqrt")
Help on built-in function sqrt in module math:

sqrt(...)
    sqrt(x)

    Return the square root of x.

>>>

Nous reviendrons plus tard sur le concept des références des fonctions. Si vous avez compris pourquoi il ne fallait pas mettre de parenthèses après le nom de la fonction dans help, tant mieux. Sinon, ce n'est pas grave, nous y reviendrons en temps voulu.

Utiliser un espace de noms spécifique

En vérité, quand vous tapez import math, cela crée un espace de noms dénommé « math », contenant les variables et fonctions du module math. Quand vous tapez math.sqrt(25), vous précisez à Python que vous souhaitez exécuter la fonction sqrt contenue dans l'espace de noms math. Cela signifie que vous pouvez avoir, dans l'espace de noms principal, une autre fonction sqrt que vous avez définie vous-mêmes. Il n'y aura pas de conflit entre, d'une part, la fonction que vous avez créée et que vous appellerez grâce à l'instruction sqrt et, d'autre part, la fonction sqrt du module math que vous appellerez grâce à l'instruction math.sqrt.

Mais, concrètement, un espace de noms, c'est quoi ?

Il s'agit de regrouper certaines fonctions et variables sous un préfixe spécifique. Prenons un exemple concret :

import math
a = 5
b = 33.2

Dans l'espace de noms principal, celui qui ne nécessite pas de préfixe et que vous utilisez depuis le début de ce tutoriel, on trouve :

  • La variable a.

  • La variable b.

  • Le module math, qui se trouve dans un espace de noms s'appelant math également. Dans cet espace de noms, on trouve :

    • la fonction sqrt ;

    • la variable pi ;

    • et bien d'autres fonctions et variables…

C'est aussi l'intérêt des modules : des variables et fonctions sont stockées à part, bien à l'abri dans un espace de noms, sans risque de conflit avec vos propres variables et fonctions. Mais dans certains cas, vous pourrez vouloir changer le nom de l'espace de noms dans lequel sera stocké le module importé.

import math as mathematiques
mathematiques.sqrt(25)

Qu'est-ce qu'on a fait là ?

On a simplement importé le module math en spécifiant à Python de l'héberger dans l'espace de noms dénommé « mathematiques » au lieu de math. Cela permet de mieux contrôler les espaces de noms des modules que vous importerez. Dans la plupart des cas, vous n'utiliserez pas cette fonctionnalité mais, au moins, vous savez qu'elle existe. Quand on se penchera sur les packages, vous vous souviendrez probablement de cette possibilité.

Une autre méthode d'importation : from … import …

Il existe une autre méthode d'importation qui ne fonctionne pas tout à fait de la même façon. En fonction du résultat attendu, j'utilise indifféremment l'une ou l'autre de ces méthodes. Reprenons notre exemple du module math. Admettons que nous ayons uniquement besoin, dans notre programme, de la fonction renvoyant la valeur absolue d'une variable. Dans ce cas, nous n'allons importer que la fonction, au lieu d'importer tout le module.

>>> from math import fabs
>>> fabs(-5)
5
>>> fabs(2)
2
>>>

Pour ceux qui n'ont pas encore étudié les valeurs absolues, il s'agit tout simplement de l'opposé de la variable si elle est négative, et de la variable elle-même si elle est positive. Une valeur absolue est ainsi toujours positive.

Vous aurez remarqué qu'on ne met plus le préfixe math. devant le nom de la fonction. En effet, nous l'avons importée avec la méthode from : celle-ci charge la fonction depuis le module indiqué et la place dans l'interpréteur au même plan que les fonctions existantes, comme print par exemple. Si vous avez compris les explications sur les espaces de noms, vous voyez que print et fabs sont dans le même espace de noms (principal).

Vous pouvez appeler toutes les variables et fonctions d'un module en tapant « * » à la place du nom de la fonction à importer.

>>> from math import *
>>> sqrt(4)
2
>>> fabs(5)
5

À la ligne 1 de notre programme, l'interpréteur a parcouru toutes les fonctions et variables du module math et les a importées directement dans l'espace de noms principal sans les emprisonner dans l'espace de noms math.

Bilan

Quelle méthode faut-il utiliser ?

Vaste question ! Je dirais que c'est à vous de voir. La seconde méthode a l'avantage inestimable d'économiser la saisie systématique du nom du module en préfixe de chaque fonction. L'inconvénient de cette méthode apparaît si l'on utilise plusieurs modules de cette manière : si par hasard il existe dans deux modules différents deux fonctions portant le même nom, l'interpréteur ne conservera que la dernière fonction appelée (je vous rappelle qu'il ne peut y avoir deux variables ou fonctions portant le même nom). Conclusion… c'est à vous de voir en fonction de vos besoins !

En résumé

  • Une fonction est une portion de code contenant des instructions, que l'on va pouvoir réutiliser facilement.

  • Découper son programme en fonctions permet une meilleure organisation.

  • Les fonctions peuvent recevoir des informations en entrée et renvoyer une information grâce au mot-clé return.

  • Les fonctions se définissent de la façon suivante : def nom_fonction(parametre1, parametre2, parametreN):

L'auteur

Découvrez aussi ce cours en...

Exemple de certificat de réussite
Exemple de certificat de réussite