Updated first chapter slides. Added new questions in the new training section.
11 KiB
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 :
- Minimaliste si possible,
- Réutilisable,
- 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 deaction
, 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 à écrirereturn 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 :
- Arguments positionnels
- 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 quekwargs == {"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 :
positionnel
(toujours premier)étoile
(utilisé positionnellement, toujours second)par défaut
(utilisé de préférence par nom, de préférence troisième)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