Licence CC BY-NC-ND, Thierry Parmentelat & Arnaud Legout

from IPython.display import HTML
HTML(filename="_static/style.html")

instructions#

principalement les structures de contrôle, et quelques-unes plus anecdotiques
nous laissons intentionnellement de coté le for pour le moment, ainsi que le try..except

l’instruction if#

L’instruction conditionnelle en Python:

if <test1>:
    <statement1>
elif <test2>:
    <statement2>
...
else:
    <statement4>

repose sur une évaluation dite paresseuse, c’est-à-dire que l’instruction s’arrête au premier test qui est vrai (on n’évalue pas les tests suivants)

# par exemple

def appreciation(note):
    if note >= 16:
        return "félicitations"
    elif note >= 14:
        return "compliments"
    elif note >= 12:
        return "encouragements"
    elif note >= 10:
        return "passable"
    else:
        return "insuffisant" 
print(appreciation(15.5))
compliments
print(appreciation(11.5))
passable

il y a aussi un match

bien que beaucoup plus tardive, il y a également une instruction match en Python, on en parle plus bas

rappel: test non booléen ?

l’expression qui détermine le test peut ne pas renvoyer un booléen - c’est très souvent le cas en pratique
dans ce cas-là on appelle la fonction bool() pour le transformer en booléen; c’est ainsi que si on doit tester si une liste est vide:

# on écrira plutôt
if L:
   print("pas vide")

# que, c'est juste aussi, mais vilain
if len(L) > 0:
   print("pas vide")

if (l’expression)#

il existe aussi une expression permettant de faire quelque chose comme if .. then .. else ..
la syntaxe générale est

<exp_1> if <test> else <exp_2>

opérateur ternaire

cette forme se rapproche de l’opérateur dit ternaire en C ou JavaScript
<test> ? <exp_1> : <exp_2>

note = 8

# et comme c'est une expression, je peux par exemple
# la passer à une fonction

print("insuffisant" if note < 10 else "ouf !")
insuffisant

boucle while#

while <test>:
    bloc d_instructions
    alignés comme on l_a vu
else:
    bloc     # exécuté à la sortie de la boucle 
    aligné   # seulement s’il n’y a pas de break

while ou for

les boucles en Python sont plus volontiers construites avec un for;
les itérations avec for sont tellement importantes en Python qu’on les traite à part

break, continue#

deux instructions particulières:

  • continue: abandonner ce tour de boucle, passer au suivant

  • break: sortir complètement de la boucle courante

on parle toujours de la boucle la plus imbriquée

# exemple avec continue
# on veut zapper les nombres pairs

L = [1, 5, 2, 7, 9]

while L:
    n = L.pop()
    if n % 2 == 0:
        continue
    # ... faire des trucs compliqués avec n
    print(f"{n=}", end=" ")
n=9 n=7 n=5 n=1 
# exemple avec break
# dès qu'on trouve un nombre pair on s'arrête

L = [1, 5, 2, 7, 9]

while L:
    n = L.pop()
    if n % 2 == 0:
        break
    # ... faire des trucs compliqués avec n
    print(f"{n=}", end=" ")
n=9 n=7 

match .. case#

une toute nouvelle instruction match permet de définir des alternatives selon:

  • la taille d’une objet

  • la valeur dans un objet

  • et en fait pas mal d’autres choses…

# dans une version à-la switch

def http_error(status):
    match status:
        case 400:
            return "Bad request"
        case 404:
            return "Not found"
        case 418:
            return "I'm a teapot"
        case 401 | 403 :
            return "Not allowed"
        case _:
            return "Something's wrong with the internet"

print(http_error(404))
Not found
# mais on peut faire aussi plus complexe
# avec du pattern-matching, ici sur la valeur
# dans un tuple

def on_values(point: tuple[float, float]):
    match point:
        case (0, 0):
            return "Origin"
        case (0, y):
            return f"null-x Y={y}"
        case (x, 0):
            return f"null-y X={x}"
        case (x, y):
            return f"regular X={x}, Y={y}"
        case _:
            raise ValueError("Not a point")

P = (0, 20)
print(on_values(P))
null-x Y=20
# ou encore ici sur la taille d'un objet

def on_size(string):
    match string.split():
        case []:
            return "no word"
        case [w]:
            return f"one word {w}"
        case first, *rest:
            return f"multi words starting with `{first}`"

print(on_size(""))
print(on_size("a b c"))
no word
multi words starting with `a`

pour une introduction plus complète, voyez ceci https://peps.python.org/pep-0636/

return#

return sert tout simplement à indiquer la fin d’une fonction, et ce qu’elle doit renvoyer
lorsque le programme atteint cette instruction, la fonction courante s’arrête immédiatement

enfin presque

il y a des instructions, notamment try et with, qui peuvent entrainer un peu de nettoyage avant qu’on sorte vraiment de la fonction

pass#

l’instruction pass ne fait rien; elle est en général utilisée lorsque la syntaxe demande une instruction, mais qu’on ne l’a pas encore implémentée

def ma_fonction():
    pass

class Foo:
    pass

libération de variables: del#

  • del a

    • instruction qui libère la variable a (en la supprimant de l’espace de nommage)

    • et décrémente le compteur de références de l’objet vers lequel pointe a

  • del fonctionne aussi sur

    • un slicing de séquence: del L[i:j:k]

    • un dictionnaire: del D[clef]

exécuter du code#

(avancé)

nous allons voir à présent deux fonctions, donc tehcniquement ce ne sont pas des instructions, mais bon ce n’est pas important

exec() et eval()#

  • comment exécuter du code dans une chaîne de caractères ?

  • exec() est une fonction qui permet l’exécution dynamique de code python

    • retourne toujours None

  • eval() est une fonction qui évalue une expression (mais pas une instruction)

    • et retourne le résultat de cette évaluation

  • le code est bien sûr passé dans un str

exec() pour une instruction#

# instructions
i1 = """def fact(n):
    return 1 if n <= 1 else n * fact(n-1)"""
i2 = """if fact(2) > 1:
    print('OUI')"""
# on peut les exécuter
# ceci ne fait rien que de définir une fonction
print(exec(i1))
None
fact
<function __main__.fact(n)>
# qu'on peut maintenant utiliser au travers de i2
# l'impression ici est faire dans le code de i2
# et puis None est le retour de exec
print(exec(i2))
OUI
None
# par contre exec() ne renvoie rien d'utile
print(exec("fact(3)"))
None

exec() vs eval()#

# expressions
e1 = '(2+3)/2.0'
e2 = "{'alice' : 35, 'bob' : 8}"
# on peut aussi les exécuter (une expression est une instruction)
# mais le retour est None ce qui perd de son intérêt
print(exec(e1))
None
# c'est mieux de les évaluer
print(eval(e1))
2.5
print(eval(e2)['alice'])
35

eval() pour une expression#

# par contre on ne peut pas évaluer une instruction
try:
    res = eval(i1)      # i1 est une instruction
except Exception as e:
    print("Exception {} - {}".format(type(e), e))
    import traceback
    traceback.print_exc()
Exception <class 'SyntaxError'> - invalid syntax (<string>, line 1)
Traceback (most recent call last):
  File "/tmp/ipykernel_838/1447414021.py", line 3, in <module>
    res = eval(i1)      # i1 est une instruction
          ^^^^^^^^
  File "<string>", line 1
    def fact(n):
    ^^^
SyntaxError: invalid syntax

use with care#

  • je recommande de n’utiliser ces deux fonctions

  • que de manière spartiate

  • souvent pas réllement utile

  • gros trou de sécurité