Retour au cours

Les fonctions

Comme des travailleurs qui travaillent pour vous

Supplément du cours 7 - en Python

Nous, les fonctions, on travaille pour vous

Il existe plusieurs fonctions dites « natives » qu'on peut utiliser en les appelant, tout simplement. Vous en employez depuis la première semaine :

print()input()len()round()abs()max()

Personne ne vous a montré le code de print. Vous l'appelez, il fait le travail : c'est tout l'intérêt.

Comment appelle-t-on un travailleur ?

Par son nom

On dit simplement son nom : round, len, max... suivi de parenthèses.

Avec du matériel

On n'oublie pas de lui donner du matériel à travailler : des données, entre les parenthèses.
3.141592le matérielround()

Appel avec valeur

nomDeLaFonction(3)
Nous avons utilisé le nom de la fonction.
Nous lui avons passé une donnée en paramètre, entre les parenthèses.

Un vrai exemple : abs(-10)

-10le paramètreabs()

Appel avec plusieurs paramètres

nomDeLaFonction(0, 0, x, y)

Nous lui avons passé 4 paramètres : 2 valeurs et 2 variables.

On sépare les paramètres par des virgules.

Un vrai exemple : max(2, 5)

25deux paramètresmax()

Appel de fonctions existantes

Sans paramètre

import random random.random()

Avec paramètres

import math math.pow(2, 4) abs(-10) max(2, 5) math.floor(2.5) math.radians(90)
C'est la fonction qui a déjà décidé son nombre de paramètres : il faut regarder la doc.

La documentation du module math :
docs.python.org/fr/3/library/math.html

abs et max sont natives ; pow, floor et radians sont rangées dans le module math, qu'on importe d'abord.

Le gaspillage

math.pow(2, 4)
abs(-10)
max(2, 5)
math.floor(2.5)
math.radians(90)
16.0
10
5
2
1.5707963267948966

C'est bien beau d'appeler des fonctions et de faire travailler les travailleurs.

Mais si on laisse cela comme cela, c'est comme laisser le résultat du travail en plan, ne pas le cueillir.

24math.pow()16.0

Chaque appel est remplacé par son résultat... qui n'est rangé nulle part. (Dans un calepin, seule la dernière ligne s'affiche ; dans un programme, rien du tout.)

Retour échappé

pow()abs()max()floor()radians()

Merci de ne pas gaspiller notre travail !!!

Le code complet

On attrape chaque résultat dans une variable

import math import random inconnu : float = random.random() puissanceDeDeux : float = math.pow(2, 4) valeurAbsolue : int = abs(-10) maximum : int = max(2, 5) plancher : int = math.floor(2.5) radian : float = math.radians(90)

C'est comme si Python avait écrit

inconnu : float = 0.37... # change à chaque appel puissanceDeDeux : float = 16.0 valeurAbsolue : int = 10 maximum : int = 5 plancher : int = 2 radian : float = 1.5707963267948966

Les données

Comme on peut le constater dans les exemples :

  • On envoie des données à la fonction entre ses parenthèses.
  • Lorsque la fonction s'est exécutée, son appel est comme remplacé par son résultat,
    • qu'on peut attraper dans une variable,
    • ou utiliser autrement : print(abs(-10)), if max(a, b) > 10:...

Appel avec variable

x : int = nomFonction(variable)
On utilise un contenant pour attraper le résultat du travail.
On ne jette plus le résultat à la poubelle.
variablenomFonction()résultatrésultatx

Dans l'ordre

Avec variable qui contient 3 et une fonction calculerCube :

PARAMÈTRE
x : int = calculerCube(variable)La valeur de variable est versée dans la fonction.
EXÉCUTION
x : int = calculerCube(3)Tout le code de la fonction s'exécute avec cette valeur.
RETOUR
x : int = 27L'appel est remplacé par la valeur retournée, le résultat du travail.
RÉCEPTION
x contient 27Le résultat est attrapé dans une variable prévue à cet effet.

Le cycle appel - exécution - réception

def calculerCube(chiffre : int) -> int: print(chiffre) return chiffre * chiffre * chiffre temps : int = 7 r : int = calculerCube(temps) print(r)

À l'appel, temps verse son contenu dans la fonction : c'est comme si une variable chiffre : int = 7 était déclarée juste dans la fonction.

Résultat

7 343

Le 7 vient du print(chiffre) dans la fonction ; le 343 est le calcul récupéré dans r.

Créer sa propre fonction

Engager un nouveau travailleur

Appel versus implémentation

L'appel

C'est l'utilisation d'une fonction existante.
resultat : int = calculerCube(5)

L'implémentation

C'est quand on invente une nouvelle fonction avec un nouveau nom. C'est dans l'implémentation qu'on écrit son code.
def calculerCube(chiffre : int) -> int: cube : int = chiffre * chiffre * chiffre return cube

Créer sa propre fonction : la syntaxe

  • On commence par le mot-clé def.
  • On lui donne un nouveau nom qui n'est pas déjà utilisé :
    • ce nom commence par un verbe ;
    • ce nom commence par une minuscule.
  • Elle comporte toujours des parenthèses, puis un deux-points.
  • Son code s'écrit en retrait (indenté) sous la première ligne : en Python, l'indentation remplace les accolades.
def calculerDouble(nombre : float) -> float: double : float = nombre * 2 return double

: float et -> float sont les indications de types vues dans le R07 : ce que la fonction reçoit et ce qu'elle retourne.

Créer sa propre fonction : direBonjour()

On va coder cette fonction de base (genre Hello World) :

def direBonjour() -> None: print("Bonjour")

Résultat

 
Ce code n'est jamais exécuté. Pourquoi ?

Créer sa propre fonction : direBonjour()

Personne n'a appelé le travailleur ! On teste notre fonction en l'appelant à partir du programme principal :

def direBonjour() -> None: print("Bonjour") direBonjour()

Résultat

Bonjour

Tester à mesure = le plus important

On doit appeler la fonction dès que son squelette est fonctionnel, AVANT d'écrire le code dedans.

def calculerMoyenne(notes : list[float]) -> float: pass # le squelette : on remplira plus tard print(calculerMoyenne([72.0, 85.0, 90.0]))

Résultat

None

pass ne fait rien : la fonction retourne None, mais l'appel fonctionne. On sait que le nom, les parenthèses et les paramètres sont bons ; on peut maintenant écrire le calcul.

Créer sa propre fonction : les données

Il faut savoir :

  • que les variables déclarées dans une fonction n'existent pas en dehors ;
  • qu'une fonction ne devrait pas se servir des variables du dehors (Python le permet en lecture, mais c'est une porte de secours, on y revient plus loin) ;
  • que la bonne manière de donner une valeur du programme principal à la fonction est de la lui passer en paramètre ;
  • que la manière de ne pas perdre le résultat d'un travail est de le retourner au programme principal avec return.

Créer sa propre fonction : afficherCarre()

On va coder cette fonction qui calcule et affiche un carré, puis la tester en lui passant un chiffre à calculer :

def afficherCarre(chiffre : int) -> None: carre : int = chiffre * chiffre print(f"Le carré est {carre}") afficherCarre(3)

Résultat

Le carré est 9

Le résultat est affiché, mais pas retourné : le programme principal ne peut plus s'en servir.

Créer sa propre fonction : calculerCube()

On va coder cette fonction qui calcule un cube et le retourne, puis la tester en lui passant un chiffre ET en récupérant le résultat :

def calculerCube(chiffre : int) -> int: cube : int = chiffre * chiffre * chiffre return cube resultat : int = calculerCube(5) print(f"Cube de 5 est {resultat}")

Résultat

Cube de 5 est 125
5calculerCube()125125resultat

Intervalle de validité

Dans le programme principal, chiffre n'existe pas

def calculerCube(chiffre : int) -> int: return chiffre * chiffre * chiffre temps : int = 7 r : int = calculerCube(temps) print(chiffre) # chiffre n'existe que dans la fonction
NameError: name 'chiffre' is not defined

Ici, temps a été copié dans chiffre le temps du travail ; ensuite chiffre disparaît.

Dans la fonction, temps est lisible... hélas

def calculerCube(chiffre : int) -> int: print(temps) # lit temps, du dehors return chiffre * chiffre * chiffre temps : int = 7 r : int = calculerCube(2)
7

En Java, c'était une erreur. Python, lui, va lire la variable du dehors : la fonction dépend alors d'un nom qu'elle ne contrôle pas, et elle cassera le jour où temps changera de nom. On passe la valeur en paramètre.

Quand devrait-on faire des fonctions ?

Quand le même algorithme revient

Quand le même algorithme est réutilisé plus d'une fois, surtout si ce n'est pas en boucle. Si tu vois du code répété, ou qui se ressemble avec de petites différences.

Quand le code mérite un titre

Quand on a assez de code pour vouloir lui donner un titre : le nom de la fonction.

Avantages et désavantage

  • Permet d'écrire le code une seule fois
  • Économise du temps de travail
  • Met de l'ordre dans le code et le documente
  • Coûte un petit peu de temps d'exécution

Préparer la factorisation

1. Repérer les redondances
2. Identifier ce qui est pareil
3. Identifier ce qui est différent
4. Mettre ce qui diffère dans des variables

Avant : le calcul est copié trois fois

temperatureMatin : float = 12.0 print(temperatureMatin * 9 / 5 + 32) temperatureMidi : float = 21.5 print(temperatureMidi * 9 / 5 + 32) temperatureSoir : float = 16.0 print(temperatureSoir * 9 / 5 + 32)

Après : ce qui diffère devient le paramètre celsius

def convertirEnFahrenheit(celsius : float) -> float: return celsius * 9 / 5 + 32 print(convertirEnFahrenheit(12.0)) print(convertirEnFahrenheit(21.5)) print(convertirEnFahrenheit(16.0))

Les deux programmes affichent exactement la même chose : 53.6, 70.7, 60.8.

À retenir

Une fonction = un travailleur

On l'appelle par son nom, on lui donne son matériel entre parenthèses.

Attraper le résultat

L'appel est remplacé par la valeur retournée : on la range dans une variable.

Engager un travailleur

def verbeEnMinuscule(parametre : type) -> type: puis le code en retrait.

Appeler, sinon rien

Une fonction jamais appelée ne s'exécute jamais. On l'appelle dès son squelette.

Les données

Ce qui entre : les paramètres. Ce qui sort : return. Le reste reste dans la fonction.

Factoriser

Le code répété devient une fonction, ce qui diffère devient un paramètre.