Programmation Python/Fonctions
Définir une fonction
modifierLa programmation est l'art d'apprendre à un ordinateur comment accomplir des tâches qu'il n'était pas capable de réaliser auparavant. L'une des méthodes les plus intéressantes pour y arriver consiste à ajouter de nouvelles instructions au langage de programmation que vous utilisez, sous la forme de fonctions originales. De plus, créer ses propres fonctions permet de factoriser le code redondant en pouvant être appelées depuis plusieurs endroits.
L'approche efficace d'un problème complexe consiste souvent à le décomposer en plusieurs sous-problèmes plus simples qui seront étudiés séparément (ces sous-problèmes peuvent éventuellement être eux-mêmes décomposés à leur tour, et ainsi de suite). Or il est important que cette décomposition soit représentée fidèlement dans les algorithmes pour que ceux-ci restent clairs.
Une fonction Python est définie par le spécificateur "def" suivi du nom de la fonction et de ses paramètres :
def nomDeLaFonction(liste de paramètres):
...
bloc d'instructions
...
return resultat
- Vous pouvez choisir n'importe quel nom pour la fonction que vous créez, à l'exception des mots réservés du langage, et à la condition de n'utiliser aucun caractère spécial ou accentué (le caractère souligné « _ » est permis). Comme c'est le cas pour les noms de variables, il vous est conseillé d'utiliser surtout des lettres minuscules, notamment au début du nom.
- Comme les instructions
if
etwhile
, l'instructiondef
est une instruction composée. La ligne contenant cette instruction se termine obligatoirement par un double point, lequel introduit un bloc d'instructions que vous ne devez pas oublier d'indenter.
- La liste de paramètres spécifie quelles informations il faudra fournir en guise d'arguments (avec leurs éventuelles valeurs par défaut) lorsque l'on voudra utiliser cette fonction (les parenthèses peuvent parfaitement rester vides si la fonction ne nécessite pas d'arguments).
- Une fonction s'utilise pratiquement comme une instruction quelconque. Dans le corps d'un programme, un appel de fonction est constitué du nom de la fonction suivi de parenthèses.
- Une fonction Python ne renvoie pas obligatoirement de résultat : le mot "return" est facultatif. S'il est absent, en termes de programmation on parlera alors plutôt de procédure que de fonction, et elle renverra "None".
- Le type d'un paramètre sera le même que celui de l'argument qui aura été transmis à la fonction. Exemple :
>>> def afficher3fois(arg):
... print arg, arg, arg
>>> afficher3fois(5)
5 5 5
>>> afficher3fois('zut')
zut zut zut
>>> afficher3fois([5, 7])
[5, 7] [5, 7] [5, 7]
>>> afficher3fois(6**2)
36 36 36
Fonctionnement
modifierdef factorielle(n):
f = 1
i = 1
while i <= n:
f = f * i
i = i + 1
return f # la valeur retournée
factorielle(7) # 5040
Récursivité
modifierUne première fonction peut appeler une deuxième fonction, qui elle-même en appelle une troisième, etc. Mais elle peut aussi s'appeler elle-même :
def factorielle(n):
if n <= 1:
return 1
else:
return n * factorielle(n-1)
factorielle(7) # 5040
Passage d'argument
modifierUne fonction accepte entre zéro et 255 d'arguments :
>>> def addition(x, y):
return x + y
addition(3, 4) # 7
>>> def multiplication(x, y):
return x * y
multiplication(3, 4) # 12
La signature est ici "x" et "y" en paramètre.
Ces arguments peuvent être des variables, mais aussi des fonctions, appelées alors "fonctions de rappel" ou "callbacks". Exemple :
>>> def operation(x, y, f):
return f(x, y)
operation(3, 4, addition) # 7
operation(3, 4, multiplication) # 12
Arguments facultatifs
modifierIl suffit de définir une valeur par défaut à un argument pour le rendre facultatif. Naturellement, cette valeur est écrasée si l'argument est précisé :
>>> def f(x = None):
if x:
print(x)
print f() # None
print f(1) # 1 None
Arguments nommés
modifierPour ne pas être obligé de remplir tous les paramètres facultatifs dans l'ordre, il est possible de n'en n'appeler que quelques-uns s'ils sont nommés :
>>> def f(p1 = 0, p2 = 0, p3):
...
f(p3 = 1) # 1
Fonction lambda
modifierUne fonction lambda est une fonction anonyme : elle n'est pas définie par def.
>>> def f(x):
return x*2
>>> f(3)
6
>>> g = lambda x: x*2 # 1
>>> g(3)
6
>>> (lambda x: x*2)(3) # 2
6
1 et 2 sont des fonctions lambda.
Récupérer les arguments de la ligne de commande
modifierLa variable sys.argv
contient les arguments de la ligne de commande, sous forme d'une liste dont le premier élément est le nom du script invoqué.
Exemple :
Si le script truc.py
contient
#!/usr/bin/python
#-*- coding: utf-8 -*-
import sys
print("Arguments : ", sys.argv)
alors l'invocation :
$ python truc.py -a rien -n=nervures
produira la sortie :
Arguments : ['truc.py', '-a', 'rien', '-n=nervures']
Si on veut récupérer l'argument n° 2 :
#!/usr/bin/python
#-*- coding: utf-8 -*-
import sys
print("Argument 2 : ", sys.argv[2])
produira la sortie :
Argument 2 : 'rien'
Déclarer les arguments
modifierimport argparse
parser = argparse.ArgumentParser()
parser.add_argument('-argument_nommé', '-a', help="description", type=str, default='valeur par défaut')
parser.add_argument('arguments non nommés', nargs='*')
print parser.parse_args()
Variables locales, variables globales
modifierLorsque nous définissons des variables à l'intérieur du corps d'une fonction, ces variables ne sont accessibles qu'à la fonction elle-même. On dit que ces variables sont des variables locales à la fonction.
En effet, chaque fois que la fonction est appelée, Python réserve pour elle (dans la mémoire de l'ordinateur) un nouvel espace de noms. Les contenus des variables locales sont stockés dans cet espace de noms qui est inaccessible depuis l'extérieur de la fonction. De plus, cet espace de noms est automatiquement détruit dès que la fonction a terminé son travail, donc si on l'appelle deux fois de suite elle recommence à zéro.
Les variables définies à l'extérieur d'une fonction sont des variables globales. Leur contenu est « visible » de l'intérieur d'une fonction, mais la fonction ne peut pas le modifier. Exemple :
>>> def mask():
... p = 20
... print p, q
...
>>> p, q = 15, 38
>>> mask()
20 38
>>> print p, q
15 38
- Analysons attentivement cet exemple
Nous commençons par définir une fonction très simple (qui n'utilise d'ailleurs aucun paramètre). À l'intérieur de cette fonction, une variable p est définie, avec 20 comme valeur initiale. Cette variable p qui est définie à l'intérieur d'une fonction sera donc une variable locale.
Une fois terminée la définition de la fonction, nous revenons au niveau principal pour y définir les deux variables p
et q
auxquelles nous attribuons les contenus 15
et 38
. Ces deux variables définies au niveau principal seront donc des variables globales.
Ainsi le même nom de variable p a été utilisé ici à deux reprises, pour définir deux variables différentes : l'une est globale et l'autre est locale. On peut constater dans la suite de l'exercice que ces deux variables sont bel et bien des variables distinctes, indépendantes, obéissant à une règle de priorité qui veut qu'à l'intérieur d'une fonction (où elles pourraient entrer en compétition), ce sont les variables définies localement qui ont la priorité.
On constate en effet que lorsque la fonction mask()
est lancée, la variable globale q
y est accessible, puisqu'elle est imprimée correctement. Pour p
, par contre, c'est la valeur attribuée localement qui est affichée.
On pourrait croire d'abord que la fonction mask()
a simplement modifié le contenu de la variable globale p
(puisqu'elle est accessible). Les lignes suivantes démontrent qu'il n'en est rien : en dehors de la fonction mask()
, la variable globale p
conserve sa valeur initiale.
Cet état de choses peut toutefois être modifié si vous le souhaitez. Il peut se faire par exemple que vous ayez à définir une fonction qui soit capable de modifier une variable globale. Pour atteindre ce résultat, il vous suffira d'utiliser l'instruction "global
". Cette instruction permet d'indiquer - à l'intérieur de la définition d'une fonction - quelles sont les variables à traiter globalement.
- Autre exemple
Dans l'exemple ci-dessous, la variable à utiliser à l'intérieur de la fonction "monter()
" est non seulement accessible, mais également modifiable, parce qu'elle est signalée explicitement comme étant une variable qu'il faut traiter globalement. Par comparaison, essayez le même exercice en supprimant l'instruction "global
" : la variable "a" n'est plus incrémentée à chaque appel de la fonction.
>>> def monter():
... global a
... a = a+1
... print a
...
>>> a = 15
>>> monter()
16
>>> monter()
17
>>>
Utilisation des fonctions dans un script
modifierPour cette première approche des fonctions, nous n'avons utilisé jusqu'ici que le mode interactif de l'interpréteur Python.
Il est bien évident que les fonctions peuvent aussi s'utiliser dans des scripts. Veuillez donc essayer vous-même le petit programme ci-dessous, lequel calcule le volume d'une sphère à l'aide de la formule que vous connaissez certainement :
def cube(n):
return n**3
def volumeSphere(r):
return 4 * 3.1416 * cube(r) / 3
r = input('Entrez la valeur du rayon : ')
print 'Le volume de cette sphère vaut', volumeSphere(r)
- Notes
À bien y regarder, ce programme comporte trois parties : les deux fonctions cube()
et volumeSphere()
, et ensuite le corps principal du programme.
Dans le corps principal du programme, il y a un appel de la fonction volumeSphere()
.
À l'intérieur de la fonction volumeSphere()
, il y a un appel de la fonction cube()
.
Notez bien que les trois parties du programme ont été disposées dans un certain ordre : d'abord la définition des fonctions, et ensuite le corps principal du programme. Cette disposition est nécessaire, parce que l'interpréteur exécute les lignes d'instructions du programme l'une après l'autre, dans l'ordre où elles apparaissent dans le code source. Dans le script, la définition des fonctions doit donc précéder leur utilisation.
Pour vous en convaincre, intervertissez cet ordre (en plaçant par exemple le corps principal du programme au début), et prenez note du type de message d'erreur qui est affiché lorsque vous essayez d'exécuter le script ainsi modifié.
En fait, le corps principal d'un programme Python constitue lui-même une entité un peu particulière, qui est toujours reconnue dans le fonctionnement interne de l'interpréteur sous le nom réservé __main__
(le mot main signifie « principal », en anglais. Il est encadré par des caractères « souligné » en double, pour éviter toute confusion avec d'autres symboles). L'exécution d'un script commence toujours avec la première instruction de cette entité __main__
, où qu'elle puisse se trouver dans le listing. Les instructions qui suivent sont alors exécutées l'une après l'autre, dans l'ordre, jusqu'au premier appel de fonction. Un appel de fonction est comme un détour dans le flux de l'exécution : au lieu de passer à l'instruction suivante, l'interpréteur exécute la fonction appelée, puis revient au programme appelant pour continuer le travail interrompu. Pour que ce mécanisme puisse fonctionner, il faut que l'interpréteur ait pu lire la définition de la fonction avant l'entité __main__
, et celle-ci sera donc placée en général à la fin du script.
Dans notre exemple, l'entité __main__
appelle une première fonction qui elle-même en appelle une deuxième. Cette situation est très fréquente en programmation. Si vous voulez comprendre correctement ce qui se passe dans un programme, vous devez donc apprendre à lire un script, non pas de la première à la dernière ligne, mais plutôt en suivant un cheminement analogue à ce qui se passe lors de l'exécution de ce script. Cela signifie concrètement que vous devrez souvent analyser un script en commençant par ses dernières lignes !
Modules de fonctions
modifierAfin que vous puissiez mieux comprendre encore la distinction entre la définition d'une fonction et son utilisation au sein d'un programme, nous vous suggérons de placer fréquemment vos définitions de fonctions dans un module Python, et le programme qui les utilise dans un autre.
- Exemple
On souhaite réaliser la série de dessins ci-dessous, à l'aide du module turtle :
Écrivez les lignes de code suivantes, et sauvegardez-les dans un fichier auquel vous donnerez le nom dessins_tortue.py :
from turtle import *
def carre(taille, couleur):
"fonction qui dessine un carré de taille et de couleur déterminées"
color(couleur)
c =0
while c <4:
forward(taille)
right(90)
c = c +1
Vous pouvez remarquer que la définition de la fonction carre()
commence par une chaîne de caractères. Cette chaîne ne joue aucun rôle fonctionnel dans le script : elle est traitée par Python comme un simple commentaire, mais qui est mémorisé à part dans un système de documentation interne automatique, lequel pourra ensuite être exploité par certains utilitaires et éditeurs « intelligents ».
Si vous programmez dans l'environnement IDLE, par exemple, vous verrez apparaître cette chaîne documentaire dans une « bulle d'aide », chaque fois que vous ferez appel aux fonctions ainsi documentées.
En fait, Python place cette chaîne dans une variable spéciale dont le nom est __doc__
(le mot « doc » entouré de deux paires de caractères « souligné »), et qui est associée à l'objet fonction comme étant l'un de ses attributs (vous en apprendrez davantage au sujet de ces attributs lorsque nous aborderons les classes d'objets).
Ainsi, vous pouvez vous-même retrouver la chaîne de documentation d'une fonction quelconque en affichant le contenu de cette variable. Exemple :
>>> def essai():
... "Cette fonction est bien documentée mais ne fait presque rien."
... print "rien à signaler"
>>> essai()
rien à signaler
>>> print essai.__doc__
Cette fonction est bien documentée mais ne fait presque rien.
Prenez donc la peine d'incorporer une telle chaîne explicative dans toutes vos définitions de fonctions futures : il s'agit là d'une pratique hautement recommandable.
Le fichier que vous aurez créé ainsi est dorénavant un véritable module de fonctions Python, au même titre que les modules turtle ou math que vous connaissez déjà. Vous pouvez donc l'utiliser dans n'importe quel autre script, comme celui-ci, par exemple, qui effectuera le travail demandé :
from dessins_tortue import *
up() # relever le crayon
goto(-150, 50) # reculer en haut à gauche
# dessiner dix carrés rouges, alignés :
i = 0
while i < 10:
down() # abaisser le crayon
carre(25, 'red') # tracer un carré
up() # relever le crayon
forward(30) # avancer + loin
i = i +1
a = input() # attendre
Vous pouvez à priori nommer vos modules de fonctions comme bon vous semble. Sachez cependant qu'il vous sera impossible d'importer un module si son nom est l'un des 29 mots réservés Python, car le nom du module importé deviendrait une variable dans votre script, et les mots réservés ne peuvent pas être utilisés comme noms de variables. Rappelons aussi qu'il vous faut éviter de donner à vos modules - et à tous vos scripts en général - le même nom que celui d'un module Python préexistant, sinon vous devez vous attendre à des conflits. Par exemple, si vous donnez le nom turtle.py à un exercice dans lequel vous avez placé une instruction d'importation du module "turtle", c'est l'exercice lui-même que vous allez importer !
Exercices
- 1.2.Définissez une fonction
ligneCar(n, ca)
qui renvoie une chaîne den
caractèresca
. - Définissez une fonction
surfCercle(R)
. Cette fonction doit renvoyer la surface (l'aire) d'un cercle dont on lui a fourni le rayon R en argument. Par exemple, l'exécution de l'instruction :
print surfCercle(2.5)
doit donner le résultat19.635
- Définissez une fonction
volBoite(x1,x2,x3)
qui renvoie le volume d'une boîte parallélépipédique dont on fournit les trois dimensionsx1
,x2
,x3
en arguments. Par exemple, l'exécution de l'instruction :
print volBoite(5.2, 7.7, 3.3)
doit donner le résultat :132.13
- Définissez une fonction
maximum(n1,n2,n3)
qui renvoie le plus grand de 3 nombresn1
,n2
,n3
fournis en arguments. Par exemple, l'exécution de l'instruction :
print maximum(2,5,4)
doit donner le résultat :5
- Complétez le module de fonctions graphiques dessins_tortue.py.
Commencez par ajouter un paramètreangle
à la fonctioncarre()
, de manière à ce que les carrés puissent être tracés dans différentes orientations. Définissez ensuite une fonctiontriangle(taille, couleur, angle)
capable de dessiner un triangle équilatéral d'une taille, d'une couleur et d'une orientation bien déterminées.
Testez votre module à l'aide d'un programme qui fera appel à ces fonctions à plusieurs reprises, avec des arguments variés pour dessiner une série de carrés et de triangles : - Ajoutez au module de l'exercice précédent une fonction
etoile5()
spécialisée dans le dessin d'étoiles à 5 branches. Dans votre programme principal, insérez une boucle qui dessine une rangée horizontale de 9 petites étoiles de tailles variées : - Ajoutez au module de l'exercice précédent une fonction
etoile6()
capable de dessiner une étoile à 6 branches, elle-même constituée de deux triangles équilatéraux imbriqués. Cette nouvelle fonction devra faire appel à la fonctiontriangle()
définie précédemment.
Votre programme principal dessinera également une série de ces étoiles : - Définissez une fonction
compteCar(ca,ch)
qui renvoie le nombre de fois que l'on rencontre le caractèreca
dans la chaîne de caractèresch
. Par exemple, l'exécution de l'instruction : print compteCar('e','Cette phrase est un exemple') doit donner le résultat : 7 - Définissez une fonction indexMax(liste) qui renvoie l'index de l'élément ayant la valeur la plus élevée dans la liste transmise en argument. Exemple d'utilisation :
serie = [5, 8, 2, 1, 9, 3, 6, 7]
print indexMax(serie)
4
- Définissez une fonction
nomMois(n)
qui renvoie le nom du ne mois de l'année.
Par exemple, l'exécution de l'instruction :
print nomMois(4)
doit donner le résultat :Avril
- Définissez une fonction
inverse(ch)
qui permette d'inverser les l'ordre des caractères d'une chaîne quelconque. (La chaîne inversée sera renvoyée au programme appelant). - Définissez une fonction
compteMots(ph)
qui renvoie le nombre de mots contenus dans la phraseph
(On considère comme mots les ensembles de caractères inclus entre des espaces).
Solution
- Réfléchissez !
-
from math import pi def surfCercle(r): "Surface d'un cercle de rayon r" return pi * r**2 # test : print surfCercle(2.5)
-
def volBoite(x1, x2, x3): "Volume d'une boîte parallélipipédique" return x1 * x2 * x3 # test : print volBoite(5.2, 7.7, 3.3)
-
def maximum(n1, n2, n3): "Renvoie le plus grand de trois nombres" if n1 >= n2 and n1 >= n3: return n1 elif n2 >= n1 and n2 >= n3: return n2 else: return n3 # test : print maximum(4.5, 5.7, 3.9)
- Réfléchissez !
- Réfléchissez !
- Réfléchissez !
-
def compteCar(ca, ch): "Renvoie le nombre de caractères ca trouvés dans la chaîne ch" i, tot = 0, 0 while i < len(ch): if ch[i] == ca: tot = tot + 1 i = i + 1 return tot # test : print compteCar("e","Cette chaîne est un exemple")
-
def indexMax(tt): "renvoie l'indice du plus grand élément de la liste tt" i, max = 0, 0 while i < len(tt): if tt[i] > max : max, imax = tt[i], i i = i + 1 return imax # test : serie = [5, 8, 2, 1, 9, 3, 6, 4] print indexMax(serie)
-
def nomMois(n): "renvoie le nom du n-ième mois de l'année" mois = ['Janvier,', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'] return mois[n -1] # les indices sont numérotés à partir de zéro # test : print nomMois(4)
- Réfléchissez !
- Réfléchissez !
Exercices
- Modifiez la fonction
volBoite(x1,x2,x3)
que vous avez définie dans un exercice précédent, de manière à ce qu'elle puisse être appelée avec trois, deux, un seul, ou même aucun argument. Utilisez pour ceux ci des valeurs par défaut égales à) 10.
Par exemple :
print volBoite()
doit donner le résultat :1000
print volBoite(5.2)
doit donner le résultat :520.0
print volBoite(5.2, 3)
doit donner le résultat :156.0
- Modifiez la fonction
volBoite(x1,x2,x3)
ci-dessus de manière à ce qu'elle puisse être appelée avec un, deux, ou trois arguments. Si un seul est utilisé, la boîte est considérée comme cubique (l'argument étant l'arête de ce cube). Si deux sont utilisés, la boîte est considérée comme un prisme à base carrée. (Dans ce cas le premier argument est le côté du carré, et le second la hauteur du prisme). Si trois arguments sont utilisés, la boîte est considérée comme un parallélépipède. Par exemple :
print volBoite()
doit donner le résultat :-1
(? indication d'une erreur).
print volBoite(5.2)
doit donner le résultat :140.608
print volBoite(5.2, 3)
doit donner le résultat :81.12
print volBoite(5.2, 3, 7.4)
doit donner le résultat :115.44
- Définissez une fonction
changeCar(ch,ca1,ca2,debut,fin)
qui remplace tous les caractèresca1
par des caractèresca2
dans la chaîne de caractèresch
, à partir de l'indicedebut
et jusqu'à l'indicefin
, ces deux derniers arguments pouvant être omis (et dans ce cas la chaîne est traitée d'une extrémité à l'autre). Exemples de la fonctionnalité attendue :>>> phrase = 'Ceci est une toute petite phrase.' >>> print changeCar(phrase, ' ', '*') Ceci*est*une*toute*petite*phrase. >>> print changeCar(phrase, ' ', '*', 8, 12) Ceci est*une*toute petite phrase. >>> print changeCar(phrase, ' ', '*', 12) Ceci est une*toute*petite*phrase. >>> print changeCar(phrase, ' ', '*', fin = 12) Ceci*est*une*toute petite phrase.
- Définissez une fonction
eleMax(liste,debut,fin)
qui renvoie l'élément ayant la plus grande valeur dans la liste transmise. Les deux arguments debut et fin indiqueront les indices entre lesquels doit s'exercer la recherche, et chacun d'eux pourra être omis (comme dans l'exercice précédent). Exemples de la fonctionnalité attendue :>>> serie = [9, 3, 6, 1, 7, 5, 4, 8, 2] >>> print eleMax(serie) 9 >>> print eleMax(serie, 2, 5) 7 >>> print eleMax(serie, 2) 8 >>> print eleMax(serie, fin =3, debut =1) 6
Solution
-
def volBoite(x1 =10, x2 =10, x3 =10): "Volume d'une boîte parallélipipédique" return x1 * x2 * x3 # test : print volBoite() print volBoite(5.2) print volBoite(5.2, 3)
-
def volBoite(x1 =-1, x2 =-1, x3 =-1): "Volume d'une boîte parallélépipédique" if x1 == -1 : return x1 # aucun argument n'a été fourni elif x2 == -1 : return x1**3 # un seul argument -> boîte cubique elif x3 == -1 : return x1*x1*x2 # deux arguments -> boîte prismatique else : return x1*x2*x3 # test : print volBoite() print volBoite(5.2) print volBoite(5.2, 3) print volBoite(5.2, 3, 7.4)
-
def changeCar(ch, ca1, ca2, debut =0, fin =-1): "Remplace tous les caractères ca1 par des ca2 dans la chaîne ch" if fin == -1: fin = len(ch) nch, i = "", 0 # nch : nouvelle chaîne à construire while i < len(ch) : if i >= debut and i <= fin and ch[i] == ca1: nch = nch + ca2 else : nch = nch + ch[i] i = i + 1 return nch # test : print changeCar("Ceci est une toute petite phrase", " ", "*") print changeCar("Ceci est une toute petite phrase", " ", "*", 8, 12) print changeCar("Ceci est une toute petite phrase", " ", "*", 12)
-
def eleMax(lst, debut =0, fin =-1): "renvoie le plus grand élément de la liste lst" if fin == -1: fin = len(lst) max, i = 0, 0 while i < len(lst): if i >= debut and i <= fin and lst[i] > max: max = lst[i] i = i + 1 return max # test : serie = [9, 3, 6, 1, 7, 5, 4, 8, 2] print eleMax(serie) print eleMax(serie, 2) print eleMax(serie, 2, 5)