# Fonctions en Python : def, return, arguments, *args et **kwargs, lambda et annotations de types

URL: https://skillaria.top/fr/guides/python/fonctions-python

Une fonction en Python est un bloc de code nommé, déclaré par le mot-clé def, appelé par son nom suivi de parenthèses et qui renvoie son résultat par return ; sans return, elle renvoie None. Les arguments se transmettent par position ou par nom, *args rassemble les valeurs positionnelles supplémentaires dans un tuple, **kwargs les valeurs nommées dans un dictionnaire, et lambda crée une petite fonction anonyme à partir d'une seule expression.

Une fonction en Python est un morceau de code qui porte un nom et peut être appelé autant de fois que nécessaire avec des données différentes. On la déclare avec le mot-clé `def`, elle reçoit des valeurs par ses paramètres et renvoie un résultat par `return`. Les fonctions sont le principal moyen de transformer un long script en un ensemble d'étapes claires et vérifiables.

## Déclarer une fonction : def en Python

La déclaration commence par `def`, suivi du nom, des paramètres entre parenthèses et d'un deux-points. Le corps est décalé de quatre espaces : l'indentation n'est pas un ornement en Python, elle fait partie de la syntaxe.

```
def saluer(nom):
    """Renvoie un message d'accueil pour l'utilisateur."""
    return f"Bonjour, {nom} !"

print(saluer("Anna"))   # Bonjour, Anna !
```

La chaîne entre triples guillemets placée juste après l'en-tête est la docstring, la documentation de la fonction. Elle s'affiche avec `help(saluer)` et dans les suggestions de l'éditeur. Selon la convention PEP 8, les noms de fonctions s'écrivent en snake_case : `ajouter_depense`, `obtenir_taux`.

La ligne `def saluer(nom):` s'appelle la signature de la fonction : le nom et la liste des paramètres (et leurs annotations, s'il y en a). À partir de la signature, le lecteur comprend comment appeler la fonction sans regarder son corps.

## Ce que fait return en Python

`return` termine la fonction et transmet une valeur à l'appelant. Le code situé après un `return` exécuté n'est pas exécuté. Si la fonction n'a pas de `return`, ou si celui-ci n'a pas de valeur, elle renvoie `None`.

```
def surface(l, h):
    l * h          # le résultat est calculé puis perdu

print(surface(3, 4))  # None

def surface(l, h):
    return l * h

print(surface(3, 4))  # 12
```

Oublier le `return` est l'une des erreurs les plus fréquentes chez les débutants : la fonction « fonctionne », mais elle renvoie `None`, et l'erreur apparaît une ligne plus loin. Une fonction peut aussi renvoyer plusieurs valeurs : il s'agit en réalité d'un seul tuple, qu'on décompose facilement :

```
def min_max(nombres):
    return min(nombres), max(nombres)

bas, haut = min_max([4, 1, 9])
print(bas, haut)   # 1 9
```

Un `return` anticipé permet d'éviter les imbrications inutiles : on traite d'abord les cas particuliers, puis la logique principale.

```
def remise(prix, est_membre):
    if prix  T:
    return elements[0]

type Montant = int | float
```

Dans Python 3.14 (PEP 649), les annotations sont devenues paresseuses : elles sont évaluées lors de leur premier accès. On peut donc citer une classe déclarée plus bas sans guillemets, et `from __future__ import annotations` n'est plus nécessaire.

## « Pas transmis » ou « None transmis » : sentinelle en 3.15

La valeur par défaut `None` ne permet pas de distinguer « l'argument n'a pas été transmis » de « on a volontairement passé None ». Pendant des années, on a contourné le problème avec une sentinelle maison :

```
_ABSENT = object()

def obtenir_taux(devise, defaut=_ABSENT):
    taux = {"USD": 90.5}
    if devise in taux:
        return taux[devise]
    if defaut is _ABSENT:
        raise KeyError(devise)
    return defaut
```

Python 3.15 introduit la fonction intégrée `sentinel` (PEP 661) : `ABSENT = sentinel("ABSENT")` crée un marqueur unique, avec une représentation lisible, qui reste identique lors d'une copie, se sérialise avec pickle et peut servir dans une annotation du type `float | ABSENT`. Sur les anciennes versions, le procédé avec `object()` reste valable.

## Erreurs typiques avec les fonctions

- **return oublié** — la fonction renvoie `None`.
- **Parenthèses oubliées** — `print(saluer)` affiche l'objet fonction, et non le résultat de l'appel.
- **Valeur par défaut modifiable** — une liste partagée par tous les appels.
- **Mauvais nombre d'arguments** — `TypeError: saluer() missing 1 required positional argument: 'nom'`.
- **Nom identique à une fonction intégrée** — une fonction nommée `list` ou `sum` masque la fonction intégrée.

## En bref

Les fonctions en Python se déclarent avec `def`, renvoient un résultat par `return`, et acceptent des arguments par position ou par nom. `*args` et `**kwargs` apportent de la souplesse, `/` et `*` imposent une rigueur d'appel, `lambda` offre la concision pour une fonction jetable, et les annotations de types, combinées à mypy, détectent les erreurs avant l'exécution. Dans le cours sur Python, ce sujet est mis en pratique : le suivi des dépenses se découpe en fonctions annotées, vérifiées par mypy.

## FAQ

**Quelle est la différence entre un paramètre et un argument ?**

Le paramètre est le nom déclaré dans la définition de la fonction, l'argument est la valeur effectivement transmise lors de l'appel. Dans def saluer(nom), nom est un paramètre ; dans saluer("Anna"), la chaîne "Anna" est un argument.

**Peut-on appeler une fonction avant de la déclarer ?**

Au niveau du module, non : la ligne def doit être exécutée avant l'appel, sinon on obtient NameError. En revanche, à l'intérieur d'une autre fonction, on peut appeler une fonction déclarée plus bas, à condition que l'appel ait lieu après sa déclaration.

**Combien de valeurs une fonction peut-elle renvoyer ?**

Formellement une seule, mais cette valeur peut être un tuple : return a, b renvoie un tuple de deux éléments qu'on décompose avec x, y = f().

**Faut-il obligatoirement écrire des annotations de types ?**

Non, Python fonctionne sans elles. Mais dans du code lu par d'autres personnes, les annotations facilitent la maintenance et permettent à mypy ou pyright de détecter les erreurs avant l'exécution.

**Peut-on écrire un if dans une lambda ?**

Seulement une expression conditionnelle : lambda x: "pair" if x % 2 == 0 else "impair". Les instructions, les boucles et les affectations ne sont pas autorisées dans une lambda.


---
Source: Skillaria.Top (https://skillaria.top)
