Aller au contenu principal

Les fonctions

Une fonction est un bloc de code qui effectue une tâche spécifique. Une fonction permet de réutiliser ce bloc de code autant de fois que nécessaire sans avoir à le réécrire. Par exemple, si vous avez un bloc de code qui affiche un message de bienvenue, vous pouvez le mettre dans une fonction et l'appeler chaque fois que vous avez besoin d'afficher ce message.

L'avantage de cette approche est que si vous voulez changer le message de bienvenue, vous n'avez qu'à le faire à un seul endroit. Tous les appels de la fonction afficheront le nouveau message.

Mais le vrai pouvoir des fonctions réside dans le fait qu'elles peuvent prendre des paramètres et retourner des valeurs. Cela permet de généraliser le code et de le rendre plus flexible, tout en gardant la caractéristique de réutilisabilité.

En Python, les fonctions sont définies en utilisant le mot-clé def suivi du nom de la fonction et des paramètres entre parenthèses. Ils sont séparés par des virgules.

def bienvenue(nom):
print(f"Bienvenue, {nom}, dans le cours de programmation objet!")

bienvenue("Léo") # "Bienvenue, Léo, dans le cours de programmation objet!"
bienvenue("Bob") # "Bienvenue, Bob, dans le cours de programmation objet!"

La fonction bienvenue prend un paramètre nom et affiche un message de bienvenue personnalisé. Vous pouvez appeler cette fonction autant de fois que vous le souhaitez avec différents noms. Elle ne spécifie aucune valeur de retour. Pour spécifier une valeur de retour, utilisez le mot-clé return.

def foo(a, b):
return a + b * 2

resultat = foo(1, 2)
print(resultat) # affiche 5

Dans cet exemple, la fonction foo prend deux paramètres a et b et retourne la valeur de a + b * 2. La valeur de retour est stockée dans la variable resultat et affichée à l'écran.

remarque

Les noms de fonctions foo, bar, baz sont des noms de fonctions génériques souvent utilisés pour illustrer des concepts. En pratique, il est préférable de donner des noms de fonctions plus descriptifs qui indiquent clairement ce que fait la fonction.

Si une fonction, comme bienvenue, ne spécifie pas de valeur de retour, elle retourne None par défaut. Ainsi, le code suivant affichera d'abord le message de bienvenue, puis None, car la fonction bienvenue ne spécifie pas de valeur de retour et donc retourne None par défaut.

resultat = bienvenue("Alice")
print(resultat)
# affiche :
# "Bienvenue, Alice, dans le cours de programmation objet!"
# "None"

Signature et corps d'une fonction

On appelle signature d'une fonction la combinaison de son nom et de ses paramètres. Par exemple, la signature de la fonction bienvenue est bienvenue(nom). La code à l'intérieur d'une fonction est appelée le corps de la fonction.

Il arrive souvent qu'un programmeur soit simplement intéressé par la signature d'une fonction, car c'est toute l'information nécessaire pour appeler la fonction.

Les paramètres sont des variables

Les paramètres sont des variables qui sont utilisées pour stocker des valeurs qui doivent être passées à la fonction.

def ma_fonction(param1, param2):
print(param1)
print(param2)

a = 1
b = 2
ma_fonction(a, b) # affiche 1 puis 2

Puisqu'ils sont des variables, il est possible d'affecter une nouvelle valeur à un paramètre de la même façon que pour une variable normale. Cela n'affectera pas la valeur de la variable passée en argument. Voici le code ci-haut modifié pour illustrer ce point.

def ma_fonction(param1, param2):
param1 = param1 + 1
# maintenant param1 vaut 1 de plus que la valeur passée en argument
print(param1)
print(param2)

a = 1
b = 2
ma_fonction(a, b) # affiche 2 puis 2
print(a) # a vaut toujours 1

La possibilité de modifier les paramètres peut être utile dans certains cas, comme dans le cas des paramètres optionnels expliqué plus bas.

Paramètres positionnels et par mot clé

Python permet de passer des paramètres à une fonction de deux façons différentes : par position et par mot clé. Les paramètres positionnels sont passés dans le même ordre que la signature de la fonction. Les exemples précédents utilisent des paramètres positionnels.

Il est aussi possible de passer des paramètres par mot clé. Dans ce cas, on spécifie le nom du paramètre suivi de la valeur à passer avec le signe = entre les deux. Cela permet de passer les paramètres dans n'importe quel ordre. Puisque le nom du paramètre est spécifié, cela rend le code plus lisible et plus facile à comprendre.

bienvenue(nom="Alice")
foo(a=1, b=2)
foo(b=2, a=1) # équivalent à foo(a=1, b=2)

Par défaut, chaque paramètre peut-être passé par position ou par mot clé. Toutefois, une fois qu'un paramètre est passé par mot clé, tous les paramètres suivants doivent aussi être passés par mot clé. Par exemple, dans le code suivant les trois premiers appels sont valides, mais le quatrième lève une erreur.

foo(1, 1)
foo(1, b=2)
foo(a=1, b=2)
foo(a=1, 2) # erreur : 2 est passé par position après un paramètre nommé

Forcer les paramètres par mot clé

Il est possible de forcer les paramètres à être passés par mot clé en utilisant une étoile * dans la signature de la fonction. Cela signifie que tous les paramètres suivants doivent être passés par mot clé. Par exemple dans le code suivant, texte peut être passé par position ou par mot clé, mais fois doit être passé par mot clé.

def repete_print(texte, *, fois):
for _ in range(fois):
print(texte)

repete_print("abc", fois=3) # OK
repete_print(texte="abc", fois=3) # OK
repete_print("abc", 3) # lève une erreur
astuce

Comme dans le code for _ in range(fois): ci-haut, vous pouvez utiliser _ comme nom de variable si vous n'avez pas besoin de la valeur. C'est une convention pour indiquer que la valeur n'est pas utilisée.

Forcer les paramètres par mot clé est un style de programmation souvent rencontré en python. Cela rend le code plus lisible et plus facile à comprendre. Un bon exemple de cette technique est la fonction print qui accepte un paramètre optionnel sep qui permet de spécifier le séparateur entre les éléments à afficher.

print(1, 2, 3, sep=" || ")  # affiche "1 || 2 || 3"
print(1, 2, 3) # affiche "1 2 3"
print(1, 2, 3, " || ") # affiche "1 2 3 || ". 'sep' n'est jamais fourni.

Un autre exemple est la fonction sorted qui accepte un paramètre optionnel key et un paramètre optionnel reverse. Ces paramètres sont utilisés pour spécifier la fonction de comparaison et l'ordre de tri respectivement.

liste = [3, 1, 2]
print(sorted(liste)) # affiche [1, 2, 3]
print(sorted(liste, reverse=True)) # affiche [3, 2, 1]

Il est impossible d'utiliser les paramètres optionnels des deux exemples ci-haut simplement par position, il faut utiliser les mots clés. Plusieurs autres fonctions de la librairie standard de python utilisent cette technique. Vous pouvez faire de même dans votre code.

Forcer les paramètres par position

Bien que plus rarement utilisé, il est possible de forcer les paramètres à être passés par position en utilisant / dans la signature de la fonction. Cela signifie que tous les paramètres précédents doivent être passés par position. Par exemple, dans le code suivant, texte doit être passé par position et fois peut être passé par position ou par mot clé.

def repete_print(texte, /, fois):
for _ in range(fois):
print(texte)

repete_print("abc", 3) # OK
repete_print("abc", fois=3) # OK
repete_print(texte="abc", fois=3) # lève une erreur

On rencontre très rarement cette technique en python, sauf avec certaines fonctions de la librairie standard pour des raisons techniques (elles sont en fait implémentées en C). Il est donc peu probable que vous ayez à utiliser cette technique dans votre code.

Il est possible de combiner les deux techniques. Dans le code suivant, param1 doit être passé par position, param2 peut être passé par position ou par mot clé et param3 doit être passé par mot clé.

def ma_fonction(param1, /, param2, *, param3):
print(param1)
print(param2)
print(param3)

ma_fonction(1, 2, param3=3) # affiche 1, 2, 3

Paramètres optionnels

Par défaut, tous les paramètres d'une fonction sont obligatoires. Cela signifie que vous devez passer une valeur pour chaque paramètre lors de l'appel de la fonction. Toutefois, il est possible de définir des paramètres optionnels en leur assignant une valeur par défaut dans la signature de la fonction. Cela permet d'appeler la fonction sans passer de valeur pour ces paramètres. C'est très utile lorsqu'on veut permettre de modifier le comportement d'une fonction tout fournissant un comportement par défaut.

def dire_bonjour(nom, salutation="Bonjour"):
print(f"{salutation}, {nom}!")

dire_bonjour("Alice") # affiche "Bonjour, Alice!"
dire_bonjour("Bob", "Salut") # affiche "Salut, Bob!"

Les paramètres optionnels doivent toujours être placés après les paramètres obligatoires. C'est logique, s'ils étaient placés avant, comment Python distinguerait-il les paramètres optionnels des paramètres obligatoires? Par exemple, si python permettait d'écrire le code suivant

def dire_bonjour(salutation='Bonjour', nom):
print(f"{salutation}, {nom}!")

alors, que signifierait dire_bonjour("Alice")? Comment Python saurait-il s'il s'agit du nom ou de la salutation ?

Il est à noter une particularité du langage python. La valeur par défaut est évaluée une seule fois lors de la définition de la fonction. Cela signifie que si la valeur par défaut est un objet mutable (comme une liste ou un dictionnaire), les modifications apportées à cet objet seront persistantes entre les appels de la fonction.

def ajouter_element(element, liste=[]):
liste.append(element)
return liste

print(ajouter_element(1)) # affiche [1]
print(ajouter_element(2)) # affiche [1, 2]

Dans cet exemple, la liste par défaut est créée une seule fois lors de la définition de la fonction. Les appels subséquents de la fonction utilisent la même liste. Pour éviter ce comportement, il est recommandé d'utiliser None et de créer une nouvelle liste à l'intérieur de la fonction.

def ajouter_element(element, liste=None):
if liste is None:
liste = []
liste.append(element)
return liste

print(ajouter_element(1)) # affiche [1]
print(ajouter_element(2)) # affiche [2]

On utilise donc astucieusement la possibilité d'affecter une valeur à un paramètre pour créer une nouvelle liste si le paramètre est None.

danger

Ne jamais utiliser un objet mutable comme valeur par défaut pour un paramètre optionnel. Cela peut causer des comportements inattendus. Utiliser None à la place et créer un nouvel objet à l'intérieur de la fonction.

Nombre variable de paramètres positionnels

Il est possible de définir une fonction qui accepte un nombre variable de paramètres positionnels en utilisant l'opérateur * devant le nom d'un paramètre. Par convention, on utilise args pour désigner ce paramètre. Il n'est pas possible d'utiliser *args dans la signature de la fonction en même temps que * pour forcer les paramètres par mot clé.

Il peut sembler étrange de définir une fonction qui accepte un nombre variable de paramètres, mais cela peut être très utile dans certains cas. Par exemple la fonction print accepte un nombre variable de paramètres et les affiche tous. Si elle prenait une liste à la place qui contient les éléments à afficher, il serait impossible de savoir s'il faut imprimer la liste (avec les crochets) ou les éléments de la liste.

Un autre cas est lorsqu'on veut écrire une fonction qui manipule des fonctions. Il est souvent utile de faire abstraction du nombre exact de paramètres de la fonction manipulée. Les décorateurs utilisent cette technique pour fonctionner sur tous les types de fonctions, peut importe le nombre de paramètres.

Néanmoins, avant de continuer plus loin, il est important de noter que dans la plupart des cas, mieux vaut prendre une liste en paramètre plutôt que d'utiliser un nombre variable de paramètres. Cela rend le code plus lisible et plus facile à comprendre. De plus, la structure de données naturelle pour stocker un nombre variable d'éléments est une liste.

astuce

À moins d'avoir une très bonne raison, mieux vaut utiliser une liste comme paramètre plutôt qu'un nombre variable de paramètres.

Un autre exemple d'utilisation de paramètres variables est la fonction sum qui calcule la somme de tous les paramètres passés. Voici une implémentation possible de cette fonction.

def somme(*args):
total = 0
for arg in args:
total += arg
return total

print(somme(1, 2, 3)) # affiche 6

Il ne peut y avoir qu'un seul paramètre variable par fonction. La fonction suivante est mal définie et lèvera une erreur.

def ma_fonction(param1, *args1, *args2):
pass

Il est possible de combiner des paramètres obligatoires, des paramètres optionnels et le paramètre variable dans une même fonction. Dans ce cas, n'importe quel ordre des paramètres est permis. Oui c'est mélangeant, car sans paramètre variable les paramètres optionnels doivent être placés après les paramètres obligatoires. La raison est qu'il n'y a plus d'ambiguïté, car le paramètre variable peut prendre tous les paramètres restants.

Dans le case simple suivant, on a deux paramètres obligatoires et un paramètre variable.

def ma_fonction(param1, param2, *args):
print(param1)
print(param2)
print(args)

ma_fonction(1, 2, 3, 4, 5) # affiche 1, 2, (3, 4, 5)
ma_fonction(1, 2) # affiche 1, 2, ()

On peut aussi combiner des paramètres optionnels et un paramètre variable. Dans le code ci-dessous, si jamais un seul paramètre est passé, le deuxième paramètre sera defaut et le paramètre variable sera vide. Si deux paramètres sont passés, le paramètre variable sera vide. Le paramètre variable sera non vide seulement si plus de deux paramètres sont passés.

def ma_fonction(param1, param2="defaut", *args):
print(param1)
print(param2)
print(args)

ma_fonction(1, 2, 3, 4, 5) # affiche 1, 2, (3, 4, 5)
ma_fonction(1) # affiche 1, defaut, ()

Bien que plus mélangeant, le code ci-dessous est parfaitement valide. La seule façon de founir une valeur au param2 est de faire un appel par mot clé. Puisque param2 n'a pas de valeur par défaut, il est obligatoire de fournir une valeur pour ce paramètre.

def ma_fonction(param1, *args, param2):
print(param1)
print(args)
print(param2)

ma_fonction(1, 2, 3, param2=4) # affiche 1, (2, 3), 4
ma_fonction(1, param2=4) # affiche 1, (), 4
ma_fonction(1, 2, 3, 4) # lève une erreur

Finalement, on peut aussi mettre des paramètres optionnels après le paramètre variable.

def ma_fonction(param1, *args, param2="defaut"):
print(param1)
print(args)
print(param2)

ma_fonction(1, 2, 3, param2=4) # affiche 1, (2, 3), 4
ma_fonction(1, 2, 3) # affiche 1, (2, 3), defaut

Nombre variable de paramètres nommés

Il est possible de définir une fonction qui accepte un nombre variable de paramètres par mot clé en utilisant l'opérateur ** devant le nom d'un paramètre. Par convention, on utilise kwargs pour désigner ce paramètre. Contraitement au paramètre positionnel variable, le paramètre par mot clé variable doit être le dernier paramètre de la fonction. De plus, sa valeur est un dictionnaire qui contient les paramètres nommés passés à la fonction. La clé est le nom du paramètre et la valeur est la valeur passée.

def repete_print(**kwargs):
fois = kwargs.get("fois", 1)
texte = kwargs.get("texte", None)
if texte is None:
return
for _ in range(fois):
print(texte)

repete_print(texte="abc", fois=3) # affiche "abc" trois fois
repete_print(fois=3, texte="abc") # affiche "abc" trois fois
repete_print(texte="abc") # affiche "abc" une fois
repete_print(fois=3) # n'affiche rien
repete_print() # n'affiche rien

Appel avec * et **

Finalement, il est possible de passer une liste ou un dictionnaire à une fonction qui accepte un nombre variable de paramètres positionnels ou par mot clé en utilisant l'opérateur * ou ** respectivement. Cela permet de déballer les éléments de la liste ou du dictionnaire et de les passer à la fonction.

def ma_fonction(param1, param2, param3):
print(param1)
print(param2)
print(param3)

args_liste = [1, 2, 3]
ma_fonction(*args_liste) # affiche 1, 2, 3

kwargs_dict = {"param1": 1, "param2": 2, "param3": 3}
ma_fonction(**kwargs_dict) # affiche 1, 2, 3

Il est aussi possible de combiner les deux techniques si vous voulez vraiment mélanger le programmeur qui lira votre code.

args_liste = [1, 2]
kwargs_dict = {"param3": 3}
ma_fonction(*args_liste, **kwargs_dict) # affiche 1, 2, 3
attention

À moins d'avoir une bonne raison, mieux vaut éviter d'utiliser * et ** pour passer des paramètres à une fonction. Cela rend le code plus difficile à lire et à comprendre.


Ancien site :

Utilité des fonctions

Les fonctions sont des blocs de code réutilisables qui permettent de structurer et d'organiser le code de manière logique. Elles facilitent la maintenance, la lisibilité et la réutilisation du code. En encapsulant des tâches spécifiques dans des fonctions, on peut éviter la duplication de code et rendre le programme plus modulaire.

Retourner plusieurs valeurs

En Python, une fonction peut retourner plus d'une valeur en utilisant des tuples. Cela permet de renvoyer plusieurs résultats à partir d'une seule fonction.

def calculer(a, b):
somme = a + b
produit = a * b
return somme, produit

s, p = calculer(3, 4)
print(f"Somme: {s}, Produit: {p}") # Affiche "Somme: 7, Produit: 12"

Appeler une fonction depuis une autre fonction

Une fonction peut appeler une autre fonction. Cela permet de décomposer des tâches complexes en sous-tâches plus simples et de réutiliser des fonctions existantes.

=== "Python"

def chauffer_eau(temperature):
print(f"Chauffage de l'eau à {temperature} degrés.")
return f"eau à {temperature} degrés"

def moudre_grains():
print("Mouture des grains de café.")
return "café moulu"

def preparer_cafe():
eau = chauffer_eau(90)
cafe = moudre_grains()
print(f"Préparation du café avec {eau} et {cafe}.")
return "café prêt"

cafe = preparer_cafe()
print(cafe)

=== "Résultat"

Chauffage de l'eau à 90 degrés.
Mouture des grains de café.
Préparation du café avec eau à 90 degrés et café moulu.
café prêt

Paramètres d'une fonction

0 ou plusieurs paramètres

Une fonction peut être définie sans paramètres ou avec plusieurs paramètres.

def saluer():
print("Bonjour!")

def additionner(a, b):
return a + b

saluer() # Affiche "Bonjour!"
print(additionner(3, 4)) # Affiche 7

Valeur par défaut

Les paramètres peuvent avoir des valeurs par défaut, ce qui permet de les omettre lors de l'appel de la fonction.

def saluer(nom="tout le monde"):
print(f"Bonjour, {nom}!")

saluer() # Affiche "Bonjour, tout le monde!"
saluer("Alice") # Affiche "Bonjour, Alice!"

!!! warning "Attention" Les objets mutables (comme les listes ou les dictionnaires) utilisés comme valeurs par défaut peuvent poser des problèmes. Ils sont partagés entre les appels de la fonction, ce qui peut entraîner des effets de bord inattendus. Mieux vaut utiliser None comme valeur par défaut et initialiser l'objet à l'intérieur de la fonction.

def ajouter_element(element, liste=[]):
liste.append(element)
return liste

print(ajouter_element(1)) # Affiche [1]
print(ajouter_element(2)) # Affiche [1, 2]

def ajouter_element2(element, liste=None):
if liste is None:
liste = []
liste.append(element)
return liste

print(ajouter_element2(1)) # Affiche [1]
print(ajouter_element2(2)) # Affiche [2]

Paramètres variables avec *args

Une fonction peut accepter un nombre variable de paramètres en utilisant *args.

def additionner_tout(*args):
return sum(args)

print(additionner_tout(1, 2, 3)) # Affiche 6
print(additionner_tout(5, 10, 15, 20)) # Affiche 50

Appel avec mot clé

Les paramètres peuvent être passés par mot clé, ce qui permet de les spécifier dans n'importe quel ordre.

def decrire_personne(nom, age, ville):
print(f"{nom} a {age} ans et habite à {ville}.")

decrire_personne(age=30, ville="Paris", nom="Alice")
# Affiche "Alice a 30 ans et habite à Paris."

Portée locale et globale

La portée d'une variable détermine où elle peut être utilisée dans le code. En Python, les variables peuvent avoir une portée locale ou globale.

Variables locales

Les variables définies à l'intérieur d'une fonction sont locales à cette fonction et ne peuvent pas être utilisées en dehors de celle-ci.

def ma_fonction():
x = 10 # Variable locale
print(x)

ma_fonction() # Affiche 10
print(x) # Erreur : x n'est pas défini en dehors de la fonction

Variables globales

Les variables définies en dehors de toutes les fonctions sont globales et peuvent être utilisées dans n'importe quelle fonction. Pour modifier une variable globale à l'intérieur d'une fonction, on doit utiliser le mot-clé global.

x = 10  # Variable globale

def ma_fonction():
global x
x = 20 # Modification de la variable globale
print(x)

ma_fonction() # Affiche 20
print(x) # Affiche 20

!!! warning "Attention" L'utilisation excessive de variables globales peut rendre le code difficile à comprendre et à maintenir. Il est recommandé de limiter l'utilisation des variables globales et de préférer les variables locales autant que possible.

Variables locales et globales avec le même nom

Si une variable locale et une variable globale ont le même nom, la variable locale masque la variable globale à l'intérieur de la fonction.

x = 10  # Variable globale

def ma_fonction():
x = 20 # Variable locale
print(x) # Affiche 20

ma_fonction()
print(x) # Affiche 10