Aller au contenu

Les fonctions en Python

Attention

Ce chapitre présente de nombreuses notions qui sont peu intuitives, et donc qui demandent potentiellement du temps et de la pratique pour être comprises. Ne soyez pas surpris si vous trouvez certains aspects un peu confus n'hesitez pas à demander de l'aide, et experimentez a minima avec les exercices.

Ce chapitre et le suivant, un peu difficiles, sont la dernière ligne droite avant de commencer à faire des programmes plus intéressants. Tenez bon !

Les logiciels font partie des "objets" les plus complexes jamais conçus par des humains.

Il nous est impossible de visualiser mentalement un logiciel de grande taille, ou même de taille moyenne dans tous les détails de son fonctionnement à la fois.

Voici un aperçu du nombre de lignes (approximatifs) de code (sloc) de certains logiciels1. Les commentaires sont exclus.

Logiciel Usage Ordre de grandeur sloc Principaux langages
OpenFlappyBird Une version Libre de Flappy Bird 600 Java
SuperTux Une version Libre de Super Mario 100K C++
Terasology Une version Libre de Minecraft 130K Java
KDenLive Editeur vidéo non-linéaire 150K C++, C
Audacity Enregistrement et traitement audio 1M Python, C, C++
CPython Interpréteur Python le plus répandu 1.5M Python, C
Blender Modélisation 3D 2.3M C++, C, Python
llvm Suite de compilation pour les langages de la famille du C. 11M C++, C, Python, LLVM IR, ASM, CMake2, Objective C
Firefox Navigateur Web 25M C++, C, Python, JavaScript, ASM, Rust

A titre de comparaison, l'intégrale du Seigneur des Anneaux compte environ 400 000 mots3.

Pour surpasser cette complexité, les langages de programmation fournissent un mécanisme que l'on utilise au quotidien dans nos langues naturelles : l'abstraction.

Une histoire de crêpes

Supposons que l'on veuille faire des crêpes, parce que c'est bon les crêpes. Une recette de crêpes ressemble au final beaucoup à un programme informatique4 :

- Dans un grand saladier, verser 1L de lait.
- Ajouter 4 oeufs. 
- Crever les jaunes et bien mélanger
- Incorporer 500g farine de blé progressivement, en mélangeant vigoureusement
- Fouettez la pâte jusqu'a quasi disparition des grumeaux
- Faire fondre 75g de beurre, et l'ajouter à la pâte
- Ajouter une pincée de sel et env une cuil. à soupe de sucre
- Fouetter encore pour bien incorporer.
- Eventuellement, laissez la pâte reposer au frais environ une heure

- Dans une poêle, faites fondre une noix de beurre.
- Attendez que le beurre brunisse légèrement
- Tant qu'il reste de la pâte : 
    - Versez une loupe de pâte dans la poêle en inclinant la poele pour étaler la pâte
    - Faites dorer la crepe a feu vif environ 30 secondes à une minute, le temps que les bords se relèvent légèrement
    - Retournez la crêpe et faites la cuire à feu doux encore 30 secondes à une minute.
    - Sortez là de la poêle, elle est prête à être dégustée.
    - Si les crêpes sont trop fines, ajoutez un peu de farine à la pâte
    - Si elles sont trop épaisses, ajoutez un peu de lait ou d'eau à la pâte
    - Si elles accrochent, rebeurrez la poêle à l'aide d'un chiffon ou d'un sopalin.

crepes

On a fait beaucoup trop de crêpes... Pour régler le problème, on peut inviter des amis, de la famille ou des collègues pour nous aider à les manger. Pour les informer qu'on a fait des crêpes, on ne leur décrit pas la recette de crêpes :

Hello, c'est Félix, ce matin
j'ai versé 1L de lait dans un grand saladier, 
puis ajouté 4 oeufs,
Crevé les jaunes et bien mélanger
Incorporé 500g de farine de blé progressivement, en mélangeant vigoureusement
...

Et j'en ai beaucoup trop, ça te dis de m'aider à les finir ?

On leur écrit, tout simplement, qu'on a fait des crêpes.

Hello, c'est Félix, ce matin j'ai fait des crêpes.
Et j'en ai beaucoup trop, ça te dis de m'aider à les finir ?

On a résumé nos actions en une phrase simple. On a exprimé en quelques mots un processus qui prends une vingtaines de lignes à être détaillé. Celà rends la communication plus rapide et donc plus efficace. Nos contacts n'auront pas à rejouer la recette dans leurs têtes, ils ont directement accès à notre intention, le pourquoi on a exécuté cette recette, et donc son résultat.

Une fonction est un des équivalents, en programmation, de ce genre de résumé.

Présentation des fonctions

De l'extérieur, on peut voir une fonction comme machine avec un mécanisme caché. On va lui donner des valeurs en entrée, des arguments, sur lesquels elle va effectuer des traitements. Eventuellement, la fonction produira une valeur en sortie, le résultat, aussi appelé valeur de retour.

On se représentera une fonction comme un rectangle à bord arrondi.

Fonction machine opaque

On a déjà utilisé des fonction sans le savoir : input et print, que l'on peut représenter graphiquement comme suit :

Vue graphique input print

Appel

Pour utiliser une fonction, on fait un appel de fonction. Pour ça, on écrit le nom de la fonction (son identifiant), puis la liste des arguments qu'on veut lui envoyer entre des parenthèses, comme suit :

nom_fonction(argument0, argument1, argument2, ...)

Si la fonction retourne une valeur, on peut l'utiliser dans une expression :

a = nom_fonction(argument0) + 22 + nom_autre_fonction(argument0, argument1)

On a bien entendu déjà vu ça avec input et print :

nom = input("nom ?" )
print("Bonjour :")
print(nom)

Le code ci-dessus peut se visualiser ainsi :

Double appel print visuel

Détaillon l'évaluation d'une expression avec des appels de fonctions.

Supposons qu'on ait la fonction suivante :

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

Et qu'on l'appelle dans une expression :

a = 2 * addition(3, 4)

Python va donc évaluer l'expression 2 * addition(3, 4).

Dans une expression, les appels de fonction passent avant presque tous les autres opérateurs, comme la multiplication et l'addition. Python va donc appeler la fonction, et utiliser son résultat à la place de l'appel.

addition(3, 4) vaut 7

Notre expression en cours d'évaluation passe donc de 2 * addition(3, 4) à 2 * 7.

Python evalue ensuite la multiplication avec l'opérateur *, et donc l'expression passe de 2 * 7 à 14.

14 est une valeur simple, donc l'évaluation est finie, et l'expression vaut 14.

Définition

Quand on crée une nouvelle fonction, on dit qu'on la définit.

Celà ce fait à l'aide de la syntaxe suivante :

def identifiant_de_la_fonction(argument0, argument1, argument2, ...):
    instructions qui constituent la fonction (indentées) : son corps

Les arguments sont alors des variables que l'on peut utiliser dans le corps de la fonction.

Par exemple, si on veut proposer une "traduction" de la fonction print, on pourrait s'y prendre comme ça :

def afficher(message):
    print(message)

Une fonction doit être obligatoirement définie avant d'être utilisée. C'est à dire, être définie plus "haut" dans le code.

def afficher(message):
    print(message)

afficher("Bonjour !")

Produit :

Bonjour !

Alors que

afficher("Bonjour !")

def afficher(message):
    print(message)

Produit une erreur :

Traceback (most recent call last):
  File "/home/felix/erreur.py", line 1, in <module>
    afficher("Bonjour !")
NameError: name 'affiche' is not defined

Si on veut qu'une fonction produise une valeur, il faut utiliser l'instruction return :

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

print(ajouter(10, 2))

Produit

12

Après un return, on sort de la fonction, donc toutes les instructions du corps de la fonction qui devraient s'executer après le return sont ignorées. On ne mettra donc JAMAIS d'instructions après un return.

def ajouter(a, b):
    return a + b
    print("je ne suis pas executée")

print(ajouter(10, 2))
12

Bien entendu, on n'est pas limités à une seule instruction par fonction ! On peut même créer des variables dans une fonction !

def demander_afficher_nom_prenom():
    a = input("nom ? ")
    b = input("prenom ? ")
    print(nom + " " + prenom)

Le nom des arguments est uniquement utile à l'intérieur du corps de la fonction, pour accéder à leur valeur. Quand on appelle une fonction, c'est la position des arguments qui compte, et pas leurs noms :

def afficher(a, b):
    print("argument a : " + str(a))
    print("argument b : " + str(b))

c = "C"
d = "D"
afficher(c,d)

print("") ## nouvelle ligne pour la lisibilité

a = "A"
b = "B"
afficher(b, a)

affiche :

argument a : C
argument b : D

argument a : B
argument b : A

Exercices

Ecrire une fonction compter qui prends un nombre n en paramètres, et affiche les n + 1 premiers entiers (zéro inclus). puis l'utiliser pour écrire un programme qui demande un nombre m à l'utilisateur, et appelle compter(m).

m ? 5
0
1
2
3
4
5
  • Commencez par déclarer et écrire la fonction
  • N'oubliez pas d'indenter son corps
  • A L'intérieur de la fonction, vous devez utiliser une boucle while.

Voici un programme à compléter :

def compter(n):
    ##Ici le code pour afficher les entiers de 0 à n inclus

m = int(input("m ? "))
##Ici le code pour appeler compter(m)
def compter(n):
    i = 0
    while i <= n:
        print(i)
        i = i + 1

m = int(input("m ? "))
compter(m)

Trace d'appels, erreurs

On a vu au premier chapitre que quand Python engendre une erreur, il nous dit quelle instruction (ligne et fichier) est à l'origine de l'erreur. C'est très pratique pour rapidement comprendre l'erreur et la résoudre.

Malheureusement, avec les fonctions, savoir quelle instruction a généré l'erreur n'est pas suffisant, puisqu'une fonction peut être appelée de plusieurs endroits dans un programme.

Quand ce programme s'execute,

1
2
3
4
5
6
7
8
def afficher_division(a, b):
    r = a / b
    print(r)

afficher_division(4, 2)
afficher_division(2, 0)
afficher_division(3, 7)
afficher_division(22, 5)

L'instruction r= a / b de la fonction afficher_division, ligne 2, va générer une erreur. Pourtant la fonction semble correcte. spoiler : elle est correcte par rapport à ce qu'on a vu pour le moment, alors on va considérer qu'elle l'est ! Mais alors, d'où vient l'erreur ?

L'erreur vient du fait qu'on a fait l'appel afficher_division(2, 0), ligne 6, qui a conduit à une division par 0 dans la fonction afficher_division. Ici, c'est notre appel qui est incorrect, pas la fonction.

Heureusement, si on regarde le message d'erreur généré par ce programme :

Traceback (most recent call last):
  File "/home/felix/erreur.py", line 6, in <module>
    afficher_division(2, 0)
  File "/home/felix/erreur.py", line 2, in afficher_division
    r = a / b
ZeroDivisionError: division by zero

On voit qu'il fait mention de l'appel afficher_division(2, 0). Pour comprendre ce message d'erreur, il faut qu'on ait une idée de ce qu'est la pile d'appel.

On peut bien entendu appeler une fonction depuis le corps d'une autre fonction.

def fonction_b():
    print("-> B")
    print("<- B")

def fonction_a():
    print("-> A")
    fonction_b()
    print("<- A") 

fonction_a()

Observez bien la sortie de ce programme :

-> A
-> B
<- B
<- A
On pourrait résumer ça comme ça : 

On appelle la fonction A
    Elle appelle la fonction B
    La fonction B retourne
La fonction A retourne

Les appels de fonction imbriqués depuis d'autres fonctions forment une pile (Comme une pile d'assiettes) :

Illustration de la pile d'appel

La fonction en haut de la pile est celle qui s'execute. Ici, c'est la fonction A.

Le haut de pile s'execute

Quand une fonction A appelle une fonction B, A met B sur la pile. C'est donc B qui s'execute, et A se met en pause en attendant que B retourne.

A appelle B

Quand B retourne, elle se retire de la pile et donc "découvre" A qui est maintenant en haut de la pile et reprends son execution là où elle s'était mise en pause.

B retourne

Le schéma suivant donne un exemple de l'évolution de la pile d'appel au long de l'execution d'un petit programme.

Schéma de pile d'appel

Quand Python génère une erreur, il affiche la pile d'appel au moment de l'erreur, la trace de pile, pour que l'on comprenne plus facilement ce qui a causé l'erreur. Cette opération s'appelle le traceback en Python.

Reprenons le code et le message d'erreur de tout à l'heure :

1
2
3
4
5
6
7
8
def afficher_division(a, b):
    r = a / b
    print(r)

afficher_division(4, 2)
afficher_division(2, 0)
afficher_division(3, 7)
afficher_division(22, 5)
Traceback (most recent call last):
  File "/home/felix/erreur.py", line 6, in <module>
    afficher_division(2, 0)
  File "/home/felix/erreur.py", line 2, in afficher_division
    r = a / b
ZeroDivisionError: division by zero

Le bloc indenté Traceback est un compte rendu de l'état de la pile au moment de l'appel, qui retrace chaque instruction qui a fait un appel présent dans la pile. Attention, la pile est à l'envers : le haut de la pile est en bas du bloc.

File "/home/felix/erreur.py", line 2, in afficher_division
  r = a / b

Nous informe que l'instruction r = a / b a causé une erreur. Cette instruction se trouve dans la fonction afficher_division, à la ligne 2 du fichier erreur.py. *Remarque : le nom du fichier peut changer, celà dépends du nom du ficher dans lequel vous executez l'exemple.

Juste au dessus,

File "/home/felix/erreur.py", line 6, in <module>
  afficher_division(2, 0)

Est l'appel qui a conduit à l'erreur, il se trouve à la ligne 6 du fichier erreur.py. <module> signifie que l'instruction est écrite directement dans le fichier, et non dans une fonction.

La valeur None

En Python, il existe une valeur spéciale, None, de type NoneType. Elle permet de signifier qu'une variable est "vide", c'est à dire qu'elle n'a pas reçu de valeur utilisable.

Nous n'utiliseront pas beaucoup cette valeur, mais il faut savoir qu'elle existe, parce que les fonction qui ne retournent pas de valeur retournent en réalité None, et celà peut être parfois un peu étrange :

def pas_de_retour(b):
    ## cette fonction ne fait strictement rien
    c = b

if pas_de_retour(10) == None:
    print("pas_de_retour a retourné None")
pas_de_retour a retourné None

La valeur None genère en général des erreurs quand on essaie de faire des opérations dessus. C'est ce qui peut arriver quand on oublie de retourner la valeur d'une fonction par exemple.

def addition(a, b):
    a + b ## oups, on a oublié return

print(addition(10, 20) * 2)
Traceback (most recent call last):
  File "/home/felix/nonetype_error.py", line 4, in <module>
    print(addition(10, 20) * 2)
TypeError: unsupported operand type(s) for *: 'NoneType' and 'int'

Retour sur le nommage

Voici quelques conseils supplémentaires pour nommer vos fonctions et variables. Vous n'êtes pas obligés de les suivre, mais c'est conseillé pour garder votre code propre, lisible, et cohérent avec celui du cours. Il y a bien entendu des cas où il est justifié de ne pas suivre ces règles :

Le nom des variables décrit ce pourquoi on les utilise et non quelles valeurs elles sont (leur type): compteur plutôt que entier ou int_compteur.

compteur = 0 ##bien, compteur sert à compter
while compteur < 10:
    ...
entier = 0 ## Pas bien, ici entier est utilisé comme un compteur
while entier < 10:
    ...
int_compteur = 0 ## Pas bien, le int est superflu
while int_compteur < 10:
    ...

Le nom des fonctions qui ne font que produire une valeur de retour à partir de leurs arguments sans les modifier est le nom de l'opération ou du résultat. def addition(a, b), plutôt que def ajouter(a,b). On les appelle les fonctions pures.

def addition(a, b):
    return a + b
def ajouter(a, b):
    return a + b

Le nom des autres fonctions (celles qui effectuent des traitements qui ne participent pas à la valeur de retour, ou ne retournent rien) sont basées sur le verbe de l'operation qu'elles effectuent. On les appelle les fonctions impures.

def saluer(nom):
    print("Bonjour, " + nom)
def afficher_addition(a, b):
    print(str(a) + " + " + str(b) + " = " str(a + b))
def bonjour(nom):
    print("Bonjour, " + nom)
def affichage_addition(a, b):
    print(str(a) + " + " + str(b) + " = " str(a + b))

Exercices

Exercice

Observez le code bout de code suivant :

def chats(n):
    print("miaou ! " * n)

A quoi sert le mot clé def ?

Il sert à définir une fonction.

Quel est le nom de la fonction définie dans ce code ?

Le nom de la fonction est chats

Combien d'arguments cette fonction a-t-elle ? Quels sont leurs noms ?

Cette fonction a un seul argument, n. La liste des arguments d'une fonction est définie entre les parenthèses.

A quoi servent les arguments d'une fonction ?

Les arguments d'une fonction servent à donner des valeurs au corps de la fonction.

Exercice

Observez le code de la fonction suivante :

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

Combien d'arguments cette fonction admet-elle ? Quels sont leurs noms ?

cette fonction admet 2 arguments : a et b.

A quoi sert le mot clé return

Le mot clé return sert à dire quelle valeur la fonction retourne

Que se passe t'il quand on appelle une fonction qui retourne une valeur dans une expression ?

Par exemple

a = addition(10, 20)
print(addition(a, b))

L'appel de la fonction est "remplacé" par la valeur que la fonction retourne dans l'expression.

a = addition(10, 20)
## Ici, a vaut 30
## affiche 30 a l'écran
print(addition(a, b))

Le programme suivant génère une erreur. Reconstituez la pile de fonctions au moment de cette erreur. Par exemple, vous pouvez la dessiner sous forme d'un tableau vertical avec dans chaque case le nom d'une fonction. Placez le haut de pile en haut du tableau.

def fonction_1(a):
    print(a + 10)        

def fonction_2(a):
    fonction_1(a)

def fonction_3(b):
    fonction_1(b)

def fonction_4(b):
    fonction_2(b)
    fonction_3(str(b))

fonction_4(10)
fonction_4("chaine")

Quelle est l'instruction qui a engendré l'erreur ?

(Difficile)

En supposant que la fonction fonction_1 est correcte, quelle est l'instruction qui est réellement la cause de l'erreur ?

  • Vous pouvez executer le code pour vous aider du message d'erreur.
  • Aidez vous de la trace d'appel (traceback) dans le message d'erreur, allez relire plus haut.

On execute le programme, par exemple à l'aide de Thonny. Il génère un message d'erreur :

Traceback (most recent call last):
File "/home/felix/erreur.py", line 14, in <module>
    fonction_4(10)
File "/home/felix/erreur.py", line 12, in fonction_4
    fonction_3(str(b))
File "/home/felix/erreur.py", line 8, in fonction_3
    fonction_1(b)
File "/home/felix/erreur.py", line 2, in fonction_1
    print(a + 10)
TypeError: can only concatenate str (not "int") to str

On va alors lire le nom des fonctions sur la droite, in fonction..., en du bas vers le haut, pour reconstituer la pile :

haut
fonction_1
fonction_3
fonction_4

L'instruction qui a engendré l'erreur est celle qui est le plus bas dans le traceback, print(a + 10). Elle setrouve ligne 2 du fichier. Vous avez peut-être une ligne différente si vous avez copié-collé le code en laissant une ou plusieurs lignes vides au dessus.

(Difficile)

L'erreur est une erreur de type TypeError, qui nous informe qu'on a essayé de concaténer une chaîne avec un entier. Il faut donc cherche d'où vient la valeur de type chaîne dans notre traceback.

  • On a d'abord appelé fonction_4 avec un entier en paramètres.
  • Puis fonction_4 a appelé fonction_3 en effectuant une conversion en chaîne, ligne 12 : fonction_3(str(b)). Il n'y a pas d'autre conversion en chaîne dans notre pile d'appel, et les fonctions ne font autrement que transmettre les paramètres aux fonctions qu'elles appellent. Si on suppose la fonction fonction_1 correcte, l'instruction ligne 12 est donc la réelle cause de l'erreur.

Exploration

Ecrivez une fonction compter qui prends un entier n >= 0 en paramètre, et affiche les nombres entiers de n à 0 par ordre décroissant.
La fonction doit absolument faire moins de 10 lignes, et vous n'avez pas le droit de déclarer d'autre fonction. Dans le corps de la fonction, la seule structure de contrôle autorisée est un if, sans else, ni elif.

compter(5)

affiche

5
4
3
2
1
0

Vous savez qu'on peut appeler une fonction depuis le corps d'une autre fonction.

Mais saviez-vous qu'une fonction peut s'appeler elle-même ?

Executez le code suivant :

def afficher():
    print("YEAAAAAAAH!")
    afficher()

afficher()

Observez bien sa sortie.

Essayez d'executer le code suivant. Observez bien sa sortie. Comment le modifier pour qu'il s'arrête à 0 ?

def afficher(m):
    print(m)
    afficher(m-1)

afficher(10)
def compter(n):
    print(n)
    # On utilise la valeur de n comme un critère d'arrêt.
    if n > 0:
        compter(n-1)

Reprenons le code de la fonction de l'exercice précédent.

def compter(n):
    print(n)
    # On utilise la valeur de n comme un critère d'arrêt.
    if n > 0:
        compter(n-1)

Essayez d'appeler compter(n), avec une très grande valeur de n. Par exemple, 1000000. Laissez tourner un peu le programme jusqu'a qu'il s'arrête. Que se passe-t-il ?

A votre avis, pourquoi ?

Relisez bien la partie du cours sur les traces d'appel.

A votre avis, où est stockée la pile d'appel ?

Après un certain temps, le programme lève une erreur.

RecursionError: maximum recursion depth exceeded while calling a Python object

Une erreur de récursion est survient quand la pile d'appel grandit trop.

La pile d'appel est stockée dans la mémoire de l'ordinateur. Comme la mémoire n'est pas infinie, les systèmes d'exploitation et les interprêteurs limitent la taille de la pile d'appel, et donc le nombre d'appel imbriqués qui peuvent être fait.

Si on fait trop d'appel, le programme est alors terminé en erreur. C'est ce qu'on appelle un dépassement de pile.

Voici un code incomplet

## vous pouvez définir d'autres fonctions.

def ap(o, a, b):
    return o(a,b)

def afficher_addition(a, b):
    m = ## remplacez ce commentaire par qqch
    print(ap(m, a, b))

afficher_addition(10, 20)

Sans modifier la fonction ap, et en ne modifiant la fonction afficher_addition qu'à l'endroit indiqué en commentaire, faites en sorte que ce programme affiche EXACTEMENT

30

Vous pouvez définir des fonctions supplémentaires si besoin.

Il y a deux méthodes.

L'une demande de bien analyser ce que fait la fonction ap.

L'autre est "très simple" mais conduit à un code très peu clair.

Executez le code suivant :

def afficher(m):
    print(m)

af = afficher

af(m)

Peut-être celà vous aidera-t-il...

En Python, on peut passer une fonction comme argument d'une autre fonction.

La fonction ap prends une fonction o et deux arguments supplémentaires a et b en entrée, et retourne le résultat de l'appel o(a,b)

On va alors créer une fonction addition et la passer en argument o à ap dans afficher_addition.

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

def ap(o, a, b):
    return o(a,b)

def afficher_addition(a, b):
    m = addition
    print(ap(m, a, b))

afficher_addition(10, 20)

En Python comme dans beaucoup d'autres langages, les fonction sont des valeurs, et peuvent être donc stockées dans des variables et passées en arguments à d'autres fonctions. Nous verrons bien plus loin dans ce cours que cette propriété permet de faire du code extrêmement concis et flexible, et fait partie d'un concept plus large qu'on appelle la généricité.

En Python, on peut redéfinir une fonction. C'est à dire définir une fonction avec le même nom qu'une fonction déjà existante. C'est alors la définition vue en dernier qui sera utilisée. On aurait donc pu redéfinir afficher_addition juste avant son appel :

## vous pouvez définir d'autres fonctions.

def ap(o, a, b):
    return o(a,b)

def afficher_addition(a, b):
    m = 0 ##ici, on met une valeur juste pour que l'interpréteur ne râle pas
    print(ap(m, a, b))

def afficher_addition(a, b):
    print(a + b)

afficher_addition(10, 20)

Redéfinir des fonctions est une en Python une mauvaise pratique, parce qu'avoir deux définitions pour une même fonction peut induire en erreur.

Implémentez une fonction addition_partielle(n), de manière à ce que l'appel suivant :

addition_partielle(10)(20)
Retourne 30.

Vous pouvez définir une fonction dans une fonction !

def afficheur(message):
    def afficher_message():
        print(message)
    return afficher_message

afm = afficheur("Hello, World !")
afm()

On va créer la fonction addition_partielle(n) de manière à ce qu'elle retourne une fonction ajout(m) qui retourne n + m.

def addition_partielle(n):
    def ajout(m):
        return n+m
    return ajout

print(addition_partielle(10)(20))

Cette capacité à créer des fonction dynamiquement, c'est à dire pendant l'execution du programme, combinée au fait que l'on peut utiliser une fonction en tant que valeur, permet dans certains cas de faire des programmes très facilement extensibles et quand même très concis !

Les essentiels

Au programme

Une fonction se déclare et se définit avec le mot clé def et la syntaxe suivante :

def nom_fonction(arg1, arg2):
    #ici des instructions
    return ar11 + arg2 #pas obligatoire

arg1 et arg2 sont les arguments ou paramètres de la fonction. Une fonction peut avoir zéro, un ou plusieurs arguments, séparés par des virgules.

On appelle une fonction avec la syntaxe suivante :

nom_fonction(val1, val2)

val1 et val2 sont des valeurs.

l'instruction return permet à la fonction de retourner une valeur, qu'on peut alors utiliser dans une expression.

a = nom_fonction(1,2) + 3

Une fonction peut appeler une autre fonction :

def fonction1():
    pass

def fonction2():
    fonction1()

Si on veut appeler une fonction, il faut qu'elle ait été définie plus haut dans le code.

Quand on appelle une fonction, c'est l'ordre des arguments qui importe, pas leurs noms.

def fonction(a,b,c):
    return (a + b) * c

a = 1
b = 100 
c = 1

r = fonction(a, c, b) #r vaut (1 + 1) *100

  1. Mesuré avec scc (https://github.com/boyter/scc) le 13/09/2022 à partir des dépôts GitHub ou GitLab, sur la branche principale. 

  2. C'est un langage pour automatiser la compilation. les fichiers de configuration pour pouvoir compiler LLVM font 73000 lignes au total. 

  3. http://lotrproject.com/statistics/books/wordscount 

  4. C'est une vrai recette de crêpes, donc vous pouvez l'utiliser pour vous régaler.