Les Fonctions en Python
Pourquoi utiliser des fonctions ?
Imaginez que vous ecrivez un programme qui calcule la TVA plusieurs fois. Sans fonctions, vous copiez-collez le meme code partout. C'est le cauchemar du developpeur : quand il faut corriger un bug, il faut le faire a dix endroits differents.
Les fonctions resument quatre principes fondamentaux :
- DRY (Don't Repeat Yourself) : ecrire le code une seule fois, l'appeler autant de fois que necessaire
- Reutilisabilite : une fonction bien ecrite peut etre utilisee dans plusieurs projets
- Lisibilite :
calculer_tva(prix)est bien plus clair que cinq lignes de calcul inline - Testabilite : on peut tester une fonction isolement, independamment du reste du programme
# Sans fonction : repetition et risque d'erreur
prix1 = 100
tva1 = prix1 * 0.20
total1 = prix1 + tva1
prix2 = 250
tva2 = prix2 * 0.20
total2 = prix2 + tva2
# Avec une fonction : propre et reutilisable
def prix_ttc(prix_ht):
tva = prix_ht * 0.20
return prix_ht + tva
total1 = prix_ttc(100)
total2 = prix_ttc(250)
Le mot-cle def et la syntaxe de base
En Python, on definit une fonction avec le mot-cle def, suivi du nom de la fonction, des parentheses, et d'un deux-points. Le corps de la fonction doit etre indente (4 espaces par convention).
def nom_de_la_fonction(parametre1, parametre2):
# corps de la fonction
instruction1
instruction2
Exemple concret :
def saluer(prenom):
message = "Bonjour, " + prenom + " !"
print(message)
Regles importantes :
- Le nom doit etre en minuscules avec des underscores (
snake_case) - L'indentation est obligatoire (Python l'utilise pour delimiter les blocs)
- La fonction n'est pas executee a sa definition, seulement quand on l'appelle
Appeler une fonction
Pour executer une fonction, on ecrit son nom suivi de parentheses avec les arguments :
def saluer(prenom):
print("Bonjour, " + prenom + " !")
# Appel de la fonction
saluer("Alice") # Affiche : Bonjour, Alice !
saluer("Bob") # Affiche : Bonjour, Bob !
saluer("Marie") # Affiche : Bonjour, Marie !
Une fonction peut etre appelee autant de fois que necessaire, avec des arguments differents a chaque fois.
Le mot-cle return
return permet a une fonction de renvoyer une valeur au code appelant. Sans return, la fonction retourne None (la valeur "rien" en Python).
def additionner(a, b):
return a + b
resultat = additionner(3, 5)
print(resultat) # 8
# Utilisation directe dans une expression
print(additionner(10, 20) * 2) # 60
Pas de return = retourne None
def afficher_carre(n):
print(n ** 2) # affiche mais ne retourne rien
valeur = afficher_carre(4) # affiche 16
print(valeur) # None
return peut aussi interrompre l'execution de la fonction :
def diviser(a, b):
if b == 0:
return None # arret immediat si division par zero
return a / b
print(diviser(10, 2)) # 5.0
print(diviser(10, 0)) # None
Parametres et arguments positionnels
Les parametres sont les variables declarees dans la definition de la fonction. Les arguments sont les valeurs passees lors de l'appel.
def presenter(prenom, age, ville):
print(f"{prenom} a {age} ans et habite a {ville}.")
presenter("Alice", 25, "Paris")
# Alice a 25 ans et habite a Paris.
L'ordre des arguments positionnels est important : le premier argument va au premier parametre, etc.
Valeurs par defaut des parametres
On peut definir une valeur par defaut pour un parametre. Si l'appelant ne fournit pas cet argument, la valeur par defaut est utilisee.
def saluer(prenom, salutation="Bonjour"):
print(f"{salutation}, {prenom} !")
saluer("Alice") # Bonjour, Alice !
saluer("Bob", "Bonsoir") # Bonsoir, Bob !
saluer("Marie", "Salut") # Salut, Marie !
Attention : les parametres avec valeur par defaut doivent toujours etre places apres les parametres sans valeur par defaut.
# Correct
def f(a, b, c=10):
pass
# Erreur de syntaxe !
# def f(a, b=10, c):
# pass
Arguments nommes (keyword arguments)
On peut passer les arguments par leur nom, ce qui permet de changer leur ordre et rend le code plus lisible :
def presenter(prenom, age, ville):
print(f"{prenom}, {age} ans, {ville}")
# Arguments nommes : l'ordre n'a pas d'importance
presenter(age=30, ville="Lyon", prenom="Claire")
# Claire, 30 ans, Lyon
# Mix positionnels et nommes (positionnels d'abord)
presenter("Paul", ville="Bordeaux", age=22)
# Paul, 22 ans, Bordeaux
*args : arguments positionnels variables
Quand on ne connait pas a l'avance le nombre d'arguments, on utilise *args. Python regroupe tous les arguments supplementaires dans un tuple.
def additionner_tout(*nombres):
total = 0
for n in nombres:
total += n
return total
print(additionner_tout(1, 2, 3)) # 6
print(additionner_tout(10, 20, 30, 40)) # 100
print(additionner_tout(5)) # 5
def afficher_infos(titre, *elements):
print(f"=== {titre} ===")
for el in elements:
print(f" - {el}")
afficher_infos("Courses", "pommes", "pain", "lait", "fromage")
# === Courses ===
# - pommes
# - pain
# - lait
# - fromage
**kwargs : arguments nommes variables
**kwargs capture les arguments nommes supplementaires dans un dictionnaire :
def afficher_profil(**infos):
for cle, valeur in infos.items():
print(f"{cle} : {valeur}")
afficher_profil(prenom="Alice", age=25, ville="Paris", metier="dev")
# prenom : Alice
# age : 25
# ville : Paris
# metier : dev
Combinaison de tout :
def fonction_complete(a, b, *args, **kwargs):
print(f"a={a}, b={b}")
print(f"args={args}")
print(f"kwargs={kwargs}")
fonction_complete(1, 2, 3, 4, 5, x=10, y=20)
# a=1, b=2
# args=(3, 4, 5)
# kwargs={'x': 10, 'y': 20}
Portee des variables (scope)
Les variables definies a l'interieur d'une fonction sont locales : elles n'existent que pendant l'execution de la fonction et disparaissent ensuite.
def ma_fonction():
variable_locale = 42
print(variable_locale) # 42
ma_fonction()
# print(variable_locale) # NameError ! variable_locale n'existe pas ici
Les variables definies en dehors des fonctions sont globales et accessibles en lecture dans les fonctions :
compteur = 0
def afficher_compteur():
print(compteur) # lecture OK
afficher_compteur() # 0
Pour modifier une variable globale depuis une fonction, il faut utiliser le mot-cle global :
compteur = 0
def incrementer():
global compteur
compteur += 1
incrementer()
incrementer()
print(compteur) # 2
L'utilisation de global est souvent signe d'une mauvaise conception. Preferez retourner des valeurs plutot que de modifier des variables globales.
Docstrings : documenter ses fonctions
Une docstring est une chaine de caracteres placee juste apres la definition de la fonction, entre triple guillemets. Elle decrit ce que fait la fonction.
def calculer_tva(prix_ht, taux=0.20):
"""
Calcule le prix TTC a partir du prix HT.
Args:
prix_ht (float): Le prix hors taxes.
taux (float): Le taux de TVA (defaut 20%).
Returns:
float: Le prix toutes taxes comprises.
"""
return prix_ht * (1 + taux)
# Accessible via help()
help(calculer_tva)
Les docstrings sont utilisees par les IDE pour l'autocompletion et par des outils comme Sphinx pour generer de la documentation automatiquement.
Fonctions lambda
Une lambda est une fonction anonyme ecrite sur une seule ligne. Elle est utile pour des operations simples et courtes, souvent passees en argument d'autres fonctions.
# Syntaxe : lambda parametres: expression
carre = lambda x: x ** 2
print(carre(5)) # 25
additionner = lambda a, b: a + b
print(additionner(3, 4)) # 7
est_pair = lambda n: n % 2 == 0
print(est_pair(4)) # True
print(est_pair(7)) # False
Les lambdas ne peuvent contenir qu'une seule expression (pas de return, pas de boucles, pas de if/else multi-lignes).
Fonctions d'ordre superieur : map(), filter(), sorted()
Python permet de passer des fonctions comme arguments d'autres fonctions. C'est la programmation fonctionnelle.
map(fonction, iterable)
Applique une fonction a chaque element d'un iterable :
nombres = [1, 2, 3, 4, 5]
carres = list(map(lambda x: x ** 2, nombres))
print(carres) # [1, 4, 9, 16, 25]
# Equivalence avec une liste comprehension :
carres = [x ** 2 for x in nombres]
filter(fonction, iterable)
Garde uniquement les elements pour lesquels la fonction retourne True :
nombres = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
pairs = list(filter(lambda x: x % 2 == 0, nombres))
print(pairs) # [2, 4, 6, 8, 10]
grands = list(filter(lambda x: x > 5, nombres))
print(grands) # [6, 7, 8, 9, 10]
sorted(iterable, key=fonction)
Trie un iterable en utilisant une fonction comme cle de tri :
etudiants = [("Alice", 17), ("Bob", 15), ("Claire", 19), ("David", 16)]
# Trier par note (deuxieme element du tuple)
par_note = sorted(etudiants, key=lambda e: e[1])
print(par_note)
# [('Bob', 15), ('David', 16), ('Alice', 17), ('Claire', 19)]
# Tri inverse
par_note_desc = sorted(etudiants, key=lambda e: e[1], reverse=True)
print(par_note_desc)
# [('Claire', 19), ('Alice', 17), ('David', 16), ('Bob', 15)]
Recursion
Une fonction est recursive quand elle s'appelle elle-meme. Toute fonction recursive doit avoir :
- Un cas de base : la condition d'arret (sinon boucle infinie)
- Un cas recursif : l'appel a elle-meme avec un probleme plus petit
Exemple : factorielle
5! = 5 × 4 × 3 × 2 × 1 = 120
5! = 5 × 4!
4! = 4 × 3!
...
1! = 1 (cas de base)
def factorielle(n):
if n <= 1: # cas de base
return 1
return n * factorielle(n - 1) # cas recursif
print(factorielle(5)) # 120
print(factorielle(10)) # 3628800
# Trace de l'execution de factorielle(4) :
# factorielle(4)
# = 4 * factorielle(3)
# = 4 * 3 * factorielle(2)
# = 4 * 3 * 2 * factorielle(1)
# = 4 * 3 * 2 * 1
# = 24
Python limite la profondeur de recursion a 1000 par defaut pour eviter les debordements de pile. Pour les grands calculs, preferez une version iterative (avec une boucle).
Exercices pratiques
Exercice 1 : Definir une fonction avec return
Utilisez des verbes ou des noms descriptifs pour nommer vos fonctions. calculer_tva(), obtenir_age(), est_valide() sont de bons noms. Evitez f(), func() ou traitement().
Exercice 2 : Fonction avec parametre par defaut
Utilisez des valeurs par defaut pour les parametres optionnels communs. Cela simplifie l'appel de la fonction dans le cas le plus frequent, tout en offrant de la flexibilite pour les cas particuliers.
Exercice 3 : Fonction lambda
Utilisez les lambdas uniquement pour des expressions courtes et simples, notamment comme argument de map(), filter() ou sorted(). Pour toute logique plus complexe, definissez une fonction normale avec def : c'est plus lisible et plus facile a tester.
Exercice 4 : Utiliser map() pour appliquer une fonction a une liste
map() et les listes en comprehension font la meme chose. En Python moderne, les listes en comprehension ([x**2 for x in nombres]) sont generalement preferees car plus lisibles. Utilisez map() quand vous passez une fonction existante deja nommee.
Exercice 5 : Utiliser filter() pour garder les elements pairs
De meme que pour map(), une liste en comprehension avec condition ([x for x in nombres if x % 2 == 0]) est souvent plus lisible que filter(). Privilegiez la lisibilite dans votre code Python.
Exercice 6 : Fonction recursive (factorielle)
Identifiez toujours clairement le cas de base avant d'ecrire le cas recursif. Sans cas de base valide, la fonction recurse indefiniment jusqu'a une erreur RecursionError. Pour les grandes valeurs, une boucle for ou while est souvent plus efficace.
Quiz de revision
Une solution
Vous devez être connecté pour voir le contenu.