Files
training.python.beginner/documentation/04-functions.md
Steve Kossouho 77aa231f5b Update first chapters and new questions
Updated first chapter slides.
Added new questions in the new training section.
2025-07-07 21:18:04 +02:00

11 KiB
Raw Blame History

title, author
title author
Découvrir les fonctions Steve Kossouho

Découvrir les fonctions


L'utilité des fonctions

Le principe d'une fonction, c'est d'être :

  1. Minimaliste si possible,
  2. Réutilisable,
  3. Un gain de temps et d'espace

Syntaxe de déclaration de fonctions

def my_simple_function():
  # Affiche un texte à chaque fois qu'on l'exécute
  print("Bonjour, vous m'avez appelé ?")
  print("Je suis une fonction très simple.")


my_simple_function()

La syntaxe permet de déclarer la fonction et dire à Python de lui associer du code.


Typographie des fonctions

Les noms de fonctions se choisissent, par convention (suivez les conventions), comme toute autre variable :

  • Tout en minuscules (ex. exponential)
  • Mots séparés par des underscores (ex. get_warning_count)
  • Peuvent contenir des chiffres sauf au début (ex. log4)

Référence versus appel de fonction

Lorsque vous déclarez une fonction appelée action :

def action():
    pass

action est une variable de type fonction dont vous pouvez ensuite exécuter le code associé.

  • action est donc la référence de la fonction.
  • action(){.python} exécute le code de action, et récupère son expression de retour.

Valeurs de retour d'une fonction

En mathématiques, une fonction renvoie toujours une valeur. Par exemple, on peut déclarer ∀ x ∈ , f(x) = x × 15{.latex}.

En Python, pour qu'une fonction qu'on déclare renvoie une valeur (transmette une valeur au code qui l'exécute) lorsqu'on l'exécute, il faut utiliser le mot-clé return{.python}.

Lorsque le mot-clé est rencontré par l'interpréteur, il interrompt immédiatement l'exécution de la fonction et la valeur est transmise comme résultat de l'appel de la fonction.

def squared(value):
    # Renvoyer la valeur élevée au carré
    return value ** 2.0
    
result = squared(10)  # on récupère 100
print(result)

Valeurs de retour et cas particuliers

Le mot-clé return{.python} a quelques comportements implicites :

  • Il peut être utilisé tout seul, sans expression : return{.python}. C'est équivalent à écrire return None{.python}, mais moins explicite.
  • Si une fonction se termine sans avoir utilisé le mot-clé return{.python} :
    • C'est toujours une fonction valide (on l'a vu dans le premier exemple)
    • Implicitement, l'interpréteur Python retourne également la valeur None{.python}.

. . .

En clair, cela signifie qu'une fonction en Python renvoie toujours une valeur qui peut être assignée à une variable, même si la valeur None{.python} a souvent peu d'utilité.


Passer quelques arguments aux fonctions

Déclarer une fonction simple, c'est déjà pas mal, mais en déclarer une qui dépend d'un ou de plusieurs arguments reçus en entrée, c'est encore plus utile !

Python propose au moins 4 types d'arguments différents, dont les usages sont évidemment différents, et parmi ceux-ci, deux sont absolument essentiels et nous allons les aborder :

  1. Arguments positionnels
  2. Arguments par défaut (avec valeur par défaut)

Arguments : Positionnels

Derrière cet adjectif un peu pompeux se cache le type d'argument le plus simple à déclarer et à utiliser :

def f(x, y):
    # Accepte deux arguments, affiche leur valeur
    # Mais ne renvoie rien d'autre que `None`
    print(x, y)
    
f(1, 2)  # exécute la fonction, donc affiche "1 2"

. . .

Passer des valeurs à ces arguments est obligatoire lorsqu'on souhaite exécuter la fonction, et les valeurs sont passées aux arguments dans le même ordre que dans la signature; ici, à la ligne 5, 1{.python} va dans x{.python} et 2{.python} va dans y{.python}. C'est de ce comportement que vient la notion de positionnalité.


Arguments : Valeurs par défaut

def f(x, alpha=15, beta=16):
    # Accepte trois arguments, dont deux avec une valeur par défaut
    print(x, alpha, beta)
    return (x, alpha, beta)  # renvoie un tuple

f(1)  # ça marche, alpha vaut 15, beta vaut 16
f(46, 10, 12)  # acceptable
f(99, alpha=23)  # conseillé
f(99, 23)  # équivalent à l'exemple du dessus
f(True, beta=23)  # conseillé et obligatoire
f(True, , 23)  # ceci est une erreur de syntaxe, on ne met pas deux virgules de suite en vrac
f(beta=23, x=50)  # passer un arg. positionnel par son nom : déconseillé

. . .

Les arguments avec valeur par défaut sont facultatifs; cela signifie que si le développeur les omet lorsqu'il exécute la fonction, Python choisira pour ces arguments leur valeur par défaut.


Arguments : ordre

Malheureusement, mais c'est techniquement nécessaire, il y a un ordre imposé par l'interpréteur Python lorsque vous déclarez les arguments acceptés par une fonction : les arguments positionnels doivent être déclarés en premier dans la liste des arguments, s'il y en a.

. . .

Tous les arguments, positionnels (obligatoires) et par défaut, peuvent cependant être passés par leur nom, mais ne faites jamais ça, vous induirez vos relecteurs en erreur :

def f(x, alpha=15, beta=16):
    return (x, alpha, beta)  # renvoyer un tuple avec x, alpha et beta

# x peut être passé par son nom, non positionnellement, mais c'est déconseillé
print(f(beta=23, x=50))  

Extra : Argument "étoile" (séquence)

Il existe un type d'argument, qui n'apparaît qu'une seule fois maximum, et dont le nom est précédé d'une étoile dans la signature de la fonction.

Cet argument apparaît après les arguments positionnels, et de forte préférence avant les arguments avec valeur par défaut. Lors de l'exécution de la fonction, cet argument contient toujours un tuple valide, même vide. Également, comme cet argument contient toujours un tuple, il est généralement nommé avec un nom au pluriel.

def stretchable(number, *words):
    print(number, words)  # words est toujours un tuple
    
stretchable(15, "word 1", "word 2", "word 3")  # les 3 arguments vont dans le tuple `words`
stretchable(15, True, 3.14159)  # les 2 arguments vont dans le tuple `words`
stretchable(1)  # le tuple `words` sera vide

Argument "étoile" : usage

Un argument de ce type accepte, lors de l'appel de la fonction, un nombre de valeurs arbitraire (aucune ou plus), et ces valeurs sont passées comme des arguments positionnels (sans nom d'argument).

Cette technique est utilisée dans la fonction print{.python} pour pouvoir afficher à la suite plusieurs arguments.

from sys import stdout

def custom_print(*values):
    for value in values:
        stdout.write(value)
        stdout.write(" ")
    stdout.write("\n")  # Échappement pour passer à la ligne

custom_print("Hello", "et", "bienvenue !")

On peut directement renseigner une liste pour ce type d'argument, en passant, lors de l'appel de fonction, une expression de liste (ou tuple) précédée par une étoile :

def function_with_extensible_args(number, *words):
    print(number, words)  # words est toujours une liste


function_with_extensible_args(15, *["word 1", "word 2", "word 3"], "word 4")  # raccourci

Extra : Argument "double-étoile" (dictionnaire)

Un dernier type d'argument, apparaît aussi une seule fois maximum, et généralement en tout dernier dans les arguments. Celui-ci s'utilise en passant des noms d'arguments qui n'existent pas ailleurs dans la signature de la fonction :

from typing import Any

def function_with_custom_named_args(other: Any = 15, **kwargs):
    print(other, kwargs)  # toujours un dictionnaire


function_with_custom_named_args(plop="Hello", foo=19)  # vont dans le dictionnaire `kwargs`.
function_with_custom_named_args(other="Hello", tornado=19)  # tornado ira dans le dictionnaire `kwargs`.

On appelle souvent cet argument l'argument [mots-clés]{.naming}, et s'appelle très fréquemment kwargs ou options.


Fonctionnement de l'argument

Si l'on appelle function_with_custom_named_args{.python} avec un argument plop, Python regarde si la fonction accepte explicitement un argument de ce nom :

  • Si oui, l'argument prendra cette valeur.
  • Si non, l'argument est ajouté comme association dans kwargs, de telle façon que kwargs == {"plop": "Hello"}{.python}.
def function_with_custom_named_args(other=15, **kwargs):
    print(other, kwargs)  # toujours un dictionnaire


function_with_custom_named_args(plop="Hello")

L'argument est de plus en plus rarement utilisé, sauf par des bibliothèques qui ont besoin d'accepter des noms calculés dynamiquement pour fonctionner.


Arguments : Ordre de déclaration

Si vous deviez avoir dans la déclaration de vos fonctions tous les types d'arguments que nous avons vus, l'ordre de leur apparition devrait être le suivant :

  1. positionnel (toujours premier)
  2. étoile (utilisé positionnellement, toujours second)
  3. par défaut (utilisé de préférence par nom, de préférence troisième)
  4. double-étoile (utilisé par nom)

Superbonus : Arguments spéciaux


Arguments spéciaux : Slash

Depuis Python 3.8 (2019), vous pouvez contrôler l'utilisation de vos fonctions en ajoutant dans votre signature un argument simplement nommé /{.python}. Il ne peut exister qu'une fois au maximum.

Lorsque vous spécifiez cet argument dans votre signature, tous les arguments qui le précèdent ne peuvent pas être spécifiés autrement que positionnellement :

def my_function(a, b, /, c, d):
    print(a, b, c, d)

my_function(1, 2, 3, 4)  # autorisé
my_function(1, 2, c=3, d=4)  # autorisé
my_function(1, b=2, c=3, d=4)  # impossible, b est positionnel uniquement

Lorsqu'un argument /{.python} est présent, les arguments *args{.python} sont interdits. La raison est que, si un tel argument est présent, on est automatiquement tenu de passer les arguments qui suivent par leur nom.


Arguments spéciaux : Étoile

Vous pouvez contrôler l'utilisation de vos fonctions en ajoutant dans votre signature un argument simplement marqué *{.python}. Il ne peut exister qu'une fois au maximum, et se trouver après l'argument /{.python}.

Lorsque vous spécifiez cet argument dans votre signature, tous les arguments qui le suivent ne peuvent être spécifiés autrement qu'en précisant leur nom :

def my_function(a, b, c=3, *, d=4):
    print(a, b, c, d)

my_function(1, 2, 3, d=4)  # autorisé
my_function(1, 2, 3, 4)  # impossible, d ne peut pas être utilisé positionnellement