Licence CC BY-NC-ND Thierry Parmentelat
_images/inria-25-alpha.png

fonctions

le mot clé def

on définit une fonction avec le mot-clé def

# remarquez:
# . l'indentation
# . le mot clé return
# . le docstring  
def P(x):
    """
    la fonction P implémente
    le polynôme
    que l'on étudie
    """
    return x**2 + 3*x + 2
# un appel
P(10)
132
P(100)
10302
# le docstring est rangé
# dans la fonction
help(P)
Help on function P in module __main__:

P(x)
    la fonction P implémente
    le polynôme
    que l'on étudie

syntaxe

en Python, les sauts de ligne et la présentation (indentation)
font partie de la syntaxe
c’est différent d’autres langages comme C++, Java, Javascript, …
ce choix est fait pour augmenter la lisibilité
car on n’a alors pas besoin de sucre syntaxique comme begin .. end ou autres { .. }

syntaxe - illustration

c’est l’indentation qui détermine la structure
l’usage est d’indenter de 4 espaces
et de ne pas utiliser de tabulations (trop variables)

// en Javascript
// on écrirait
function foo(i) {
    if (i <= 0) {
        fonction1(i);
        fonction2(i);
    } else {
        fonction3(i);
    }
}
# en Python ce serait
def foo(i):
    if i <= 0:
        fonction1(i)
        fonction2(i)
    else:
        fonction3(i)

mot-clé if

forme générale

if exp1:
    ...
    ...
elif exp2:
    ...
    ...
else:
    ...
    ...
note = 14
appreciation = None

if note >= 16:
    appreciation = 'félicitations'
elif note >= 10:
    appreciation = 'reçu'
else:
    appreciation = 'recalé'
 appreciation
'reçu'

boucle while

forme générale

while exp:
    ...
    ...
n = 132
log = 0

while n >= 1:
    log = log + 1
    n = n // 2
log
8

return

une fonction est censée retourner quelque chose

resultat = fonction(arguments)

avec return on indique ce qui est le résultat
l’exécution de la fonction s’arrête à ce moment-là
si pas de return, le retour est None

# une fonction incomplète
def broken_abs(n):
    if n <= 0:
        return -n
# avec un négatif  
broken_abs(-10)
10
# ici la fonction retourne None
# du coup le notebook n'affiche rien
broken_abs(10)
def fixed_abs(n):
    if n <= 0:
        return -n
    return n
# on ignore la dernière ligne
# puisqu'on est arrivé au return
fixed_abs(-10)
10
fixed_abs(10)
10

variables locales

à l’intérieur d’une fonction on peut naturellement utiliser des variables
la portée de ces variables est limitée à la fonction
ici les deux variables var sont des entités distinctes

var = "globale"

def polynom(n):
    """
    polynome 4.x3 + 3.x2 + 2x + 1
    sans mise à la puissance
    """
    var = n         # var = n
    resultat = 1
    resultat += 2 * var
    var = var * n   # var = n**2
    resultat += 3 * var
    var = var * n   # var = n**3
    resultat += 4 * var
    print(f"dans def: var = {var}")
    return resultat
polynom(1)
dans def: var = 1
10
polynom(10)
dans def: var = 1000
4321
var
'globale'

appels imbriqués / récursion

bien sûr dans le code d’une fonction
on peut appeler d’autres fonctions
y compris la fonction courante : fonction récursive (cf fact.py)

lorsque f appelle g,
f est en quelque sorte mise en suspens pendant l’exécution de g
du coup il est nécessaire de conserver où en est f

  • à quel point on en est dans f

  • la valeur des variables locales de f

pile d’exécution

c’est le propos de la pile d’exécution
qui conserve la trace des appels imbriqués

illustrons cela avec https://pythontutor.com/
un site qui est très utile pour visualiser l’exécution de code simple

# une magie pour créer des cellules sous pythontutor.com

%load_ext ipythontutor
%%ipythontutor height=500

def fact(n):
    if n <= 1:
        return n
    else:
        return n * fact(n-1)

# pour visualiser la pile d'exécution

x = fact(3)

exceptions

le mot-clé raise permet de lever une exception
cela a pour effet d’interrompre la fonction courante
et de dépiler les appels jusqu’à
trouver un except qui attrape l’exception

# une fonction qui va faire raise
# mais pas tout de suite
def time_bomb(n):
    print(f"in time_bomb({n})")
    if n > 0:
        return time_bomb(n-1)
    else:
        raise OverflowError("BOOM")
# si personne n'attrape un raise
# le contrôle retourne à l'OS
# d'une manière très abrupte
def driver():
    time_bomb(1)
    print("will never pass here")

driver()
in time_bomb(1)
in time_bomb(0)
---------------------------------------------------------------------------
OverflowError                             Traceback (most recent call last)
/tmp/ipykernel_2247/3279229454.py in <module>
      6     print("will never pass here")
      7 
----> 8 driver()

/tmp/ipykernel_2247/3279229454.py in driver()
      3 # d'une manière très abrupte
      4 def driver():
----> 5     time_bomb(1)
      6     print("will never pass here")
      7 

/tmp/ipykernel_2247/3588744217.py in time_bomb(n)
      4     print(f"in time_bomb({n})")
      5     if n > 0:
----> 6         return time_bomb(n-1)
      7     else:
      8         raise OverflowError("BOOM")

/tmp/ipykernel_2247/3588744217.py in time_bomb(n)
      6         return time_bomb(n-1)
      7     else:
----> 8         raise OverflowError("BOOM")

OverflowError: BOOM

uncaught

# cette fois tout est
# sous contrôle
def driver_try():
    try:
        time_bomb(2)
    except Exception as exc:
        print(f"OOPS {type(exc)}, {exc}")
    print("will do this")

driver_try()
in time_bomb(2)
in time_bomb(1)
in time_bomb(0)
OOPS <class 'OverflowError'>, BOOM
will do this

try

clause except

  • la clause raise doit fournir un objet idoine
    ne peut pas par exemple faire raise 1

  • doit être une instance d’un objet de type BaseException
    (ou de l’une de ses sous-classes)

  • la clause except permet de n’attraper
    qu’une partie des exceptions possibles

passage d’arguments

les mécanismes de définition et de passage de paramètres sont assez complexes (cf cours avancé)
pour cette introduction disons simplement qu’on peut définir des paramètres optionnels :

# une fonction qui accepte un ou deux arguments
def foo(obligatoire, optionnel=10):
    print(f"obligatoire={obligatoire} optionnel={optionnel}")
# avec deux arguments
foo(100, 20)
obligatoire=100 optionnel=20
# ou avec un seul
foo(1000)
obligatoire=1000 optionnel=10

On verra un peu plus loin comment manipuler un nombre variable d’arguments; mais pour cela on a besoin d’etudier d’abord les types évolués de Python.

exercices

https://nbhosting.inria.fr/auditor/notebook/python-mooc:exos/w4/w4-s3-x1-pgcd
https://nbhosting.inria.fr/auditor/notebook/python-mooc:exos/w4/w4-s3-x4-power

écrire une fonction qui calcule la puissance entière

def power(x, n):
    """
    retourne x à la puissance n
    en O(log(n))
    """
    pass # votre code ici

écrire une fonction qui calcule de pgcd

def pgcd(a, b):
    """
    retourne le pgcd de a et b
    par convention on admet que
    pgcd(0, n) == pgcd(n, 0) = n
    """
    pass