8. VERS L’ABSTRACTION

8.1. Fonctions: maths ou pas maths ?

Voici un exemple de définition de fonction simple et son utilisation, il s’agit d’une fonction d’élévation au carré:

def carre(x):
  y = x**2
  return y

# ici nous ne sommes plus dans la définition de la fonction,
# on a repris une indentation normale.
a = 3 # Et c'est en fait ici avec 'a = 3' que "démarre" notre programme.
b = 4
c = carre(a) + carre(b)
print c

Une fonction au sens de la programmation n’est pas une fonction au sens mathématique, tout comme a = 3 n’est pas une équation mais une affectation. Dans un programme, une fonction est une séquence d’instructions qui sera définie une fois et qui pourra être exécutée plusieurs fois, avec différentes valeurs de paramètres, et renvoyer des résultats qui pourront être utilisés pour faire d’autres calculs.

Dans l’exemple précédent, la partie définition de la fonction est:

def carre(x):
  y = x**2
  return y

Remarquer le : et l’indentation dans la définition.

La partie utilisation est:

a = 3
b = 4
c = carre(a) + carre(b)
print c

Pour indiquer que cette partie n’est pas dans la définition de la fonction, on reprend une indentation de même niveau que celle de def carre(x):.

Note

Une expression comme carre(a) se nomme un appel ou encore une invocation de fonction.

Les calculs peuvent aussi se faire directement dans l’instruction return. La fonction précédente pourrait s’écrire:

def carre(x):
  return x**2

Warning

La partie définition d’une fonction est prise en compte par l’interpréteur Python, mais ne donne lieu par elle-même à aucune exécution. Un programme ne contenant que des définitions de fonctions ne ferait donc rien. Pour qu’un traitement soit effectué, il faut qu’à un endroit du programme, en dehors de toute définition, il y ait au moins une instruction ou une séquence d’instructions, comme dans l’exemple précédent, la séquence:

a = 3
b = 4
c = carre(a) + carre(b)
print c

D’autres façons d’appeler cette fonction carre(x) sont possibles, en utilisant moins de variables intermédiaires:

a = 3
print carre(a) + carre(4)

Voire, pour cet exemple, pas variable du tout:

print carre(3) + carre(4)

Voici une autre fonction simple, mais à deux paramètres cette fois:

def hypothenuse(x,y):
  z = (x**2+y**2)**0.5
  return z

a = 3
b = 4
c = hypothenuse(a,b)
print c

Un programme complet de calcul de la somme des n premiers termes de la suite définie par:

U_0 = 8.0

U_k = U_{k-1}*U_{k-1}/(U_{k-1}+1)

def calculTermeDeRang(k):
  x = 8.0 # le .0 pour imposer un flottant et avoir ensuite des calculs en flottants
  for i in range(1,k+1):
    y = x*x/(x+1)
    x = y
  return x

n = input('donner le rang (>= 0): ')
s = 0
for i in range(n+1):
  s = s + calculTermeDeRang(i)

print 'la somme des termes du rang 0 au rang', n, 'est égale à', s

8.2. Fonctions: autres formes et combinaisons

Une fonction peut renvoyer plusieurs résultats simultanément:

def sommeEtProduit(x,y):
  somme = x+y
  produit = x*y
  return somme, produit

s,p = sommeEtProduit(3,4)
print "la somme vaut", s
print "le produit vaut", p

Une fonction peut en appeler une autre:

def plus(a,b):
  c = a + b
  return(c)

def fois2(x):
  y = plus(x,x)
  return(y)

z = 4
d = fois2(z)
print fois2(z)

Une fonction peut ne renvoyer aucun résultat:

def etoiles(n):
  print '*' * n # rappel: un chaîne multipliée par un nombre répète la chaîne

print "Quelques étoiles ..."
etoiles(10)
etoiles(20)

Une fonction peut n’avoir aucun paramètre:

def g():
  return 9.81

def poids(masse):
  return masse*g()

p = poids(12.8)
print p

Une fonction peut n’avoir ni paramètre ni résultat:

def dixEtoiles():
  print '*' * 10

print "Quelques étoiles ..."
dixEtoiles()
dixEtoiles()

8.3. Fonctions: variables locales et globales

Warning

Chaque exécution de fonction a son propre dictionnaire local d’identificateurs pour stocker ses variables et ses paramètres. Les variables et les paramètres ne sont donc pas partagés entre les fonctions. Ces variables sont dites locales à la fonction qui les utilise.

Ainsi le programme suivant est correct:

def plus(a,b):
  c = a + b
  return(c)

def fois2(a):
  c = plus(a,a)
  return(c)

z = 4
d = fois2(z)
print d

En effet les variables a et c apparaissant dans la fonction plus et les variables a et c apparaissant dans la fonction fois2 sont pour l’interpréteur Python des variables complètement différentes.

Warning

Pour une même fonction, le dictionnaire local des identificateurs est recréé à chaque appel de la fonction. Dans une fonction, les valeurs des variables ne subsistent donc pas d’un appel à l’autre de cette fonction.

Les variables crées en dehors des fonctions ne sont quant à elles pas locales mais globales. Dans l’exemple précédent, l’exécution commence par la ligne z = 4 et les variables utilisées dans la partie:

z = 4
d = fois2(z)
print d

c’est à dire z et d, sont globales, dans le sens où elles peuvent être vues et utilisées de tous les endroits du programme (même dans les fonctions).

Warning

En général, l’utilisation de variables globales conduit à des interférences voulues ou non voulues entre les variables. Ceci rend les programmes difficiles à lire et cause des erreurs souvent très longues à localiser. Pour ces raisons l’utilisation des variables globales sera proscrite (sauf cas exceptionnel).

Afin d’éviter la création de telles variables globales, on utilisera une structure de programme où subsiste en dehors des fonctions une seule instruction, celle-ci ayant pour unique rôle de lancer l’exécution des traitements. L’exemple précédent s’écrira alors:

def plus(a,b):
  c = a + b
  return(c)

def fois2(a):
  c = plus(a,a)
  return(c)

def main(): # fonction "principale"
  z = 4
  d = fois2(z)
  print d

main() # instruction pour lancer l'exécution des calculs

Warning

Pour la structure générale d’un programme voir Pour faciliter la relecture et la correction d’un programme.

Note

(AV) Pour comprendre et/ou réutiliser des programmes Python faisant usage de variables globales, consulter la documentation de référence Python à ce sujet, et voir notamment l’instruction global.

8.4. Listes: l’art de la manipulation

Voici les principales notations et fonctions permettant de manipuler des listes, présentées au travers de quelques exemples très pragmatiques ...

a = [2,3,41,0,2]

a est une liste de 5 entiers.

b = []

b est une liste vide.

La fonction len permet de connaitre la taille d’une liste. Ici len(a) donne le nombre d’éléments de a, c’est à dire 5. La liste b étant vide, len(b) donne 0.

La position des éléments dans une liste (ou parle d’indice) est numérotée à partir de zéro. Ainsi la taille de la liste [2,3,41,0,2] est de 5, mais il n’y a pas d’élément d’indice 5. En effet le premier élément a pour indice 0, et le dernier a pour indice 4.

Consultation de l’élément d’indice i:

a[i]

Modification de l’élément d’indice i:

a[i] = 7

Insertion d’un élément à l’indice i (l’élément qui s’y trouvait sera après l’insertion décalé à l’indice i+1):

a[i:i] = [ELEMENT_A_INSERER]

Exemple:

a = [2,7,4,2]

a[2:2] = [70]

la nouvelle valeur de a, après l’insertion de 70 est:

[2,7,70,4,2]

L’insertion de plusieurs éléments en une seule fois est possible:

a = [2,7,4,2]

a[2:2] = [70,8,1]

la valeur de a est alors:

[2,7,70,8,1,4,2]

Suppression de l’élément d’indice i:

a.pop(i)

Lors de la suppression on peut récupérer la valeur de l’élément supprimé:

x = a.pop(i)

Ici x contient alors l’élément qui a été supprimé de la liste a.

Suppression et récupération du dernier élément d’une liste avec pop sans paramètre:

x = a.pop()

Ajout d’un élément en bout de liste avec append:

a = [2,7,4,2]
a.append(99)
print a

donne:

[2,7,4,2,99]

Création d’une liste contenant plusieurs nombres identiques:

a = [0.0]*20

ceci crée une liste a de 20 flottants égaux à 0.0

Note

Le sens de la multiplication * est ici très proche du la multiplication d’une chaîne de caractères par un scalaire.

Création d’une liste contenant les nombres entiers de 0 inclus à n exclu:

a = range(n)

... revoir Listes: la fonction range() pour plus de possibilités.

L’opérateur + permet de concaténer deux listes:

a = [2,7,4]
b = [18,2]
c = a + b
print c

donne:

[2,7,4,18,2]

Pour parcourir une liste, rappelons nous que le for utilise une liste, on peut donc tout à fait écrire:

a = [2,3,41,0,2]
for x in a:
  print x

qui donnera à l’écran, ligne par ligne: 2 puis 3 puis 41 puis 0 et enfin de nouveau 2.

Pour vérifier la présence d’un élément dans une liste, il n’est pas obligatoire de la parcourir, l’opérateur in permet d’effectuer ce test d’appartenance:

a = [2,3,41,0,2]
x = 41
if x in a:
  print x, 'est dans la liste', a

et ce programme affichera 41 est dans la liste [2,3,41,0,2]

Note

  • un liste peut être affichée directement avec print (comme vu dans l’exemple d’ajout avec append()).
  • les listes permettent de représenter des vecteurs de composantes et des tableaux.
  • une liste peut contenir d’autres éléments que des nombres, par exemple des chaînes de caractères, ou encore d’autres listes (ce qui permettra de représenter des matrices).
  • une liste peut contenir des éléments de types différents.
  • une chaîne de caractères peut se consulter comme une liste (une liste caractères). Par exemple pour un chaîne s, on peut écrire for x in s, if x in s, len(s). Il est aussi possible d’accéder au contenu de s avec la notation [i], par exemple x=s[2] permet de récupérer le troisième caractère de s, mais attention modifier s avec s[2]=x n’est pas permis.

De nombreuses autres fonctions existent en Python pour la manipulation des listes (par exemple sur les listes de nombres pour trouver la valeur du plus petit élément min(maListe), du plus grand max(maListe), calculer la somme des éléments sum(maListe), ou encore trier une liste maListe.sort()). Les fonctions présentées dans cette section forment toutefois un noyau suffisant à partir duquel il est possible de programmer tous les traitements souhaités.

8.5. Les modules: instructions générales d’importation

Il est possible de renommer un module lors de son importation. Exemple:

import random as r

Pour accéder aux éléments du module, on emploie alors ce nouveau nom:

x = r.randrange(10)

A l’aide de la directive from, on peut réaliser des importations sans avoir à spécifier ensuite le nom du module lors de l’utilisation de ses éléments. Exemple:

from math import sin
from random import *

ceci permet d’invoquer directement la fonction sin ainsi que n’importe quel élément du module random (grâce à la notation import *):

x = sin(y)
z = randrange(20)

Warning

Il convient d’être vigilant lors de l’utilisation de ces types d’importations car il peut en résulter des problèmes de lisibilité du code si l’on change le nom d’un module standard avec ‘import ... as ...’, et il y a aussi des risques de conflits entre identificateurs si l’on utilise ‘from ... import ...’. Toutefois, ces notations sont à connaître car elles se rencontrent assez fréquemment dans les documentations et allègent l’usage de certains modules.

Il est possible d’écrire soi-même ses propores modules. Un programme monProgramme.py que l’on a écrit peut s’importer dans un autre programme ou en mode Python interactif, avec la commande:

import monProgramme

Par défaut, pour que cela fonctionne, le programme monProgramme.py doit se trouver dans le même dossier que le programme dans lequel on fait le import. Pour utiliser monProgramme.py dans le mode Python interactif, l’interpréteur doit avoir été lancé dans un terminal depuis le même dossier que celui où réside monProgramme.py. (AV) D’autres possibilités existent, notamment pour importer des modules qui se trouvent dans d’autres dossiers (si besoin consulter la documentation).

Lors de l’importation le programme monProgramme est exécuté et ensuite ses fonctions restent disponibles en utilisant la syntaxe d’appel suivante: monProgramme.nomDeFonction(...)

C’est en fait exactement la même notation que celle que l’on emploie pour les modules inclus dans la bibliothèque Python:

import math
y = math.sin(x)

Note

(AV)

from monProgramme import *

importe tous les identificateurs globaux définis dans monProgramme, sauf ceux commençant par _