Essayer de coder ← Retour aux sujets

Définir et appeler

def / return

Une fonction regroupe des instructions sous un nom : on la définit une fois avec def, on l'appelle autant de fois que nécessaire.

def f(x):
Définir une fonction (le corps est indenté)
return x * 2
Renvoyer une valeur à l'appelant
f(3)
Appeler la fonction avec l'argument 3
def g(a, b=0):
Paramètre avec valeur par défaut
g(b=5, a=3)
Arguments nommés, dans n'importe quel ordre
lambda x: x + 1
Fonction anonyme réduite à une expression
carre.py
def carre(n):
    return n ** 2

print(carre(7))
Affichage
49
💡 NSI

*args et **kwargs permettent de recevoir un nombre quelconque d'arguments sans tous les lister dans la signature.

Paramètres et valeurs de retour

signature

Ce qu'une fonction reçoit (paramètres) et ce qu'elle renvoie (return) forment sa signature : c'est ce qu'on doit connaître pour l'utiliser.

def f(a, b):
Paramètres positionnels : passés dans l'ordre
f(b=2, a=1)
Paramètres nommés : explicites
def f(x, y=10):
Valeur par défaut (après les autres paramètres)
def f(*args):
args : tuple des arguments en trop
def f(**kwargs):
kwargs : dictionnaire des arguments nommés en trop
return a, b
Renvoie deux valeurs (un tuple)
min_max.py
def min_max(valeurs):
    return min(valeurs), max(valeurs)

petit, grand = min_max([3, 9, 1, 7])
print(petit, grand)
Affichage
1 9

⚙️ Le cours : les fonctions

Une fonction est un bloc d'instructions auquel on donne un nom. Elle évite de recopier du code (on l'écrit une fois, on l'appelle partout), rend un programme lisible (le nom dit ce que fait le bloc) et permet de tester chaque morceau séparément. On la voit comme une boîte noire : des entrées (les paramètres) et une sortie (la valeur renvoyée).

Définir et appeler une fonction

On définit une fonction avec def nom(parametres): suivi d'un bloc indenté. Les noms entre parenthèses dans la définition sont les paramètres ; les valeurs données lors de l'appel sont les arguments. Définir une fonction ne l'exécute pas : le corps ne s'exécute qu'à chaque appel.

saluer.py
def saluer(prenom):
    print("Bonjour", prenom)

saluer("Ada")
saluer("Lin")
Affichage
Bonjour Ada
Bonjour Lin

return : renvoyer une valeur

return valeur termine immédiatement la fonction et renvoie la valeur à l'endroit de l'appel, où on peut la ranger dans une variable ou l'utiliser dans un calcul. Sans return, une fonction renvoie None. Ne confonds pas print (qui affiche à l'écran) et return (qui donne un résultat).

return.py
def carre(n):
    return n * n

def afficher_carre(n):
    print(n * n)

a = carre(6) + 1
b = afficher_carre(6)
print(a, b)
Affichage
36
37 None

Une fonction peut contenir plusieurs return : le premier atteint met fin à l'exécution. C'est une façon claire de traiter les cas particuliers d'abord.

signe.py
def signe(x):
    if x > 0:
        return "positif"
    if x < 0:
        return "négatif"
    return "nul"

print(signe(5), signe(-2), signe(0))
Affichage
positif négatif nul

Paramètres positionnels, nommés et par défaut

À l'appel, on peut passer les arguments dans l'ordre (positionnels) ou par nom (hauteur=5), dans n'importe quel ordre. Un paramètre peut avoir une valeur par défaut : on peut alors ne pas le donner. Les paramètres avec valeur par défaut se placent après les autres.

defauts.py
def aire(largeur, hauteur=1):
    return largeur * hauteur

print(aire(3, 4))
print(aire(3))
print(aire(hauteur=5, largeur=2))
Affichage
12
3
10

Renvoyer plusieurs valeurs

return a, b, c renvoie en réalité un tuple. À l'appel, on peut le « déballer » dans plusieurs variables d'un coup, ou le garder en entier.

stats.py
def stats(valeurs):
    return min(valeurs), max(valeurs), sum(valeurs) / len(valeurs)

mini, maxi, moyenne = stats([12, 15, 9, 14])
print(mini, maxi, moyenne)
resultat = stats([1, 2, 3])
print(resultat, type(resultat))
Affichage
9 15 12.5
(1, 3, 2.0) <class 'tuple'>

*args et **kwargs

Un paramètre précédé de * (par convention *args) rassemble tous les arguments positionnels en trop dans un tuple ; précédé de ** (**kwargs), il rassemble les arguments nommés dans un dictionnaire. À l'appel, l'étoile fait l'inverse : f(*liste) déplie une liste en arguments séparés.

args_kwargs.py
def moyenne(*notes):
    return sum(notes) / len(notes)

def afficher(**infos):
    for cle, valeur in infos.items():
        print(cle, "=", valeur)

print(moyenne(12, 15, 9))
afficher(nom="Ada", age=36)

valeurs = [4, 8, 12]
print(moyenne(*valeurs))
Affichage
12.0
nom = Ada
age = 36
8.0

La portée des variables

Une variable créée dans une fonction est locale : elle n'existe que pendant l'appel. Une variable créée en dehors est globale : une fonction peut la lire, mais si elle lui affecte une valeur, elle crée en réalité une nouvelle variable locale qui la masque.

portee.py
x = 10

def lire():
    return x + 1

def masquer():
    x = 99
    return x

print(lire(), masquer(), x)
Affichage
11 99 10

Pour modifier vraiment une variable globale, il faut la déclarer avec global. C'est possible, mais déconseillé : mieux vaut passer les valeurs en paramètres et récupérer le résultat avec return, ce qui rend la fonction prévisible et testable.

global.py
compteur = 0

def incrementer():
    global compteur
    compteur += 1

incrementer()
incrementer()
print(compteur)
Affichage
2

Attention aux objets modifiables (listes, dictionnaires) : l'argument est une référence vers le même objet, donc la fonction peut le modifier en place, sans global ni return.

effet_de_bord.py
def ajouter_zero(liste):
    liste.append(0)

nombres = [1, 2]
ajouter_zero(nombres)
print(nombres)
Affichage
[1, 2, 0]

Documenter : docstring et annotations

La première ligne d'une fonction peut être un texte entre triples guillemets : la docstring. Elle décrit ce que fait la fonction ; help(fonction) ou fonction.__doc__ l'affiche. Les annotations (rayon: float, -> float) indiquent les types attendus, mais Python ne les vérifie pas : elles servent à la lecture et aux outils.

docstring.py
def aire_disque(rayon: float) -> float:
    """Renvoie l'aire d'un disque de rayon donné."""
    return 3.14159 * rayon ** 2

print(aire_disque(2))
print(aire_disque.__doc__)
Affichage
12.56636
Renvoie l'aire d'un disque de rayon donné.

Les fonctions sont des valeurs : lambda, key, map et filter

En Python, une fonction est un objet comme un autre : on peut la ranger dans une variable ou la passer en paramètre à une autre fonction. lambda x: expression crée une petite fonction anonyme, très utile avec le paramètre key de sorted et de max, ou avec map / filter.

fonction_parametre.py
def appliquer(f, valeurs):
    return [f(v) for v in valeurs]

print(appliquer(lambda x: x * 2, [1, 2, 3]))
print(appliquer(len, ["ab", "cde"]))

eleves = [("Ada", 18), ("Lin", 15), ("Max", 17)]
print(sorted(eleves, key=lambda e: e[1], reverse=True))
print(max(eleves, key=lambda e: e[1]))
print(list(map(lambda x: x * 2, [1, 2, 3])))
print(list(filter(lambda x: x % 2 == 0, range(10))))
Affichage
[2, 4, 6]
[2, 3]
[('Ada', 18), ('Max', 17), ('Lin', 15)]
('Ada', 18)
[2, 4, 6]
[0, 2, 4, 6, 8]

La récursivité

Une fonction récursive s'appelle elle-même, avec un problème plus petit. Elle a toujours besoin d'un cas de base qui s'arrête sans appel récursif, sinon elle ne se termine jamais. Python limite la profondeur des appels (environ 1000) : au-delà, c'est une RecursionError.

recursivite.py
def factorielle(n):
    if n <= 1:
        return 1
    return n * factorielle(n - 1)

def somme_chiffres(n):
    if n < 10:
        return n
    return n % 10 + somme_chiffres(n // 10)

print(factorielle(5), factorielle(10))
print(somme_chiffres(2845))
Affichage
120 3628800
19

Le calcul de Fibonacci s'écrit presque comme sa définition mathématique, mais cette version recalcule les mêmes valeurs un très grand nombre de fois (son coût est exponentiel) : elle ne convient que pour de petits n.

fibonacci.py
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print([fibonacci(i) for i in range(10)])
Affichage
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Tester une fonction avec assert

assert condition ne fait rien si la condition est vraie et lève une AssertionError sinon. Quelques assert écrits à côté d'une fonction prouvent qu'elle donne les bons résultats sur des cas connus, et préviennent si on la casse plus tard.

tests.py
def est_pair(n):
    return n % 2 == 0

assert est_pair(4)
assert not est_pair(7)
assert est_pair(0)
print("tests réussis")
Affichage
tests réussis

⚠️ Erreurs fréquentes

Oublier le return

Sans return, la fonction calcule peut-être le bon résultat, mais elle le jette et renvoie None.

À éviter
faux.py
def double(x):
    x * 2

print(double(4))
Affichage
None
Correct
correct.py
def double(x):
    return x * 2

print(double(4))
Affichage
8

print à la place de return

Une fonction qui affiche son résultat ne peut pas le fournir à un calcul : l'appel vaut None.

À éviter
faux.py
def carre(n):
    print(n * n)

total = carre(3) + 1
Erreur
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
Correct
correct.py
def carre(n):
    return n * n

total = carre(3) + 1
print(total)
Affichage
10

Une valeur par défaut modifiable

La valeur par défaut d'un paramètre est évaluée une seule fois, à la définition. Avec une liste, tous les appels partagent donc la même liste. On utilise None comme valeur par défaut et on crée la liste dans la fonction.

À éviter
faux.py
def ajouter(x, liste=[]):
    liste.append(x)
    return liste

print(ajouter(1))
print(ajouter(2))
Affichage
[1]
[1, 2]
Correct
correct.py
def ajouter(x, liste=None):
    if liste is None:
        liste = []
    liste.append(x)
    return liste

print(ajouter(1))
print(ajouter(2))
Affichage
[1]
[2]

Oublier les parenthèses de l'appel

carre désigne la fonction elle-même ; carre(5) l'appelle. Sans parenthèses, on manipule l'objet fonction, pas son résultat.

À éviter
faux.py
def carre(n):
    return n * n

resultat = carre
print(resultat + 1)
Erreur
TypeError: unsupported operand type(s) for +: 'function' and 'int'
Correct
correct.py
def carre(n):
    return n * n

resultat = carre(5)
print(resultat + 1)
Affichage
26

Un nombre d'arguments incorrect

À éviter
faux.py
def carre(n):
    return n * n

print(carre(2, 3))
Erreur
TypeError: carre() takes 1 positional argument but 2 were given
Correct
correct.py
def carre(n):
    return n * n

print(carre(2), carre(3))
Affichage
4 9

Modifier une variable globale sans global

Affecter une variable dans une fonction en fait une variable locale : total += x essaie de lire un total local qui n'a pas encore de valeur (UnboundLocalError). Passe plutôt la valeur en paramètre et renvoie le résultat.

À éviter
faux.py
total = 0

def ajouter(x):
    total += x

ajouter(5)
Erreur
UnboundLocalError: cannot access local variable 'total' where it is not associated with a value
Correct
correct.py
def ajouter(total, x):
    return total + x

print(ajouter(0, 5))
Affichage
5

Une récursion sans cas de base

Sans cas de base, la fonction s'appelle indéfiniment jusqu'à la RecursionError (profondeur maximale dépassée). Écris toujours d'abord le cas où la fonction s'arrête.

À éviter
faux.py
def compte_a_rebours(n):
    print(n)
    compte_a_rebours(n - 1)

compte_a_rebours(3)
Correct
correct.py
def compte_a_rebours(n):
    if n == 0:
        print("décollage")
        return
    print(n)
    compte_a_rebours(n - 1)

compte_a_rebours(3)
Affichage
3
2
1
décollage

📋 Aide-mémoire

SyntaxeRôle
def f(a, b): puis un bloc indentédéfinir une fonction
return valeurrenvoyer une valeur et terminer la fonction (sans return : None)
return a, brenvoyer un tuple, à déballer avec x, y = f(...)
def f(a, b=0):paramètre par défaut (après les paramètres obligatoires)
f(b=2, a=1)arguments nommés
def f(*args, **kwargs):arguments en trop : tuple et dictionnaire ; à l'appel, f(*liste, **dico) les déplie
lambda x: x + 1fonction anonyme d'une expression
global nommodifier une variable globale (à éviter)
"""description"""docstring : première instruction du corps, lue par help
assert conditionvérifier un résultat attendu
  • Un paramètre est dans la définition, un argument est dans l'appel.
  • print affiche, return renvoie : une fonction qui doit fournir un résultat utilise return.
  • Une variable affectée dans une fonction est locale ; une liste ou un dictionnaire passé en argument peut être modifié en place.
  • Valeur par défaut modifiable ([], {}) : à remplacer par None.
  • Fonction récursive : cas de base d'abord, puis appel sur un problème plus petit ; profondeur limitée (~1000).
  • sorted(x, key=...), max(x, key=...), map, filter prennent une fonction en paramètre.