Nous avons précédemment fait appel aux variables logiques, en sélectionnant des éléments dans un tableau. Le contrôle de flux utilise également des variables logiques pour permettre à différents codes d'être exécutés selon que certaines conditions sont remplies ou non. Le contrôle de flux en Python se présente sous deux formes: instruction conditionnelle et les boucles.
Python utilise des modifications d’espace pour indiquer le début et la fin des blocs de contrôle de flux, l’indention est donc importante. Par exemple, lors de l'utilisation des blocs if. . . elif. . . else
, tous les blocs de contrôle doivent avoir le même niveau d'indentation et toutes les instructions à l'intérieur des blocs de contrôle doivent avoir le même niveau d'indentation. Revenir au niveau d'indentation précédent indique à Python que le bloc est terminé. La meilleure pratique consiste à utiliser uniquement des espaces (et non des tabulations) et 4 espaces lors du démarrage d'un nouveau niveau d'indentation. Ce qui représente un équilibre raisonnable entre lisibilité et gaspillage d'espace.
if ... elif ... else
¶Les blocs if ... elif ... else
commencent toujours par une instruction if
immédiatement suivie d'une expression logique scalaire. elif
et else
sont facultatifs et peuvent toujours être replacés par des instructions if
imbriquées aux dépens d'une logique plus complexe et d'une imbrication plus profonde. La forme générique d'un block if ... elif ... else
est donnée par
if logical_1:
Code to run if logical_1
elif logical_2:
Code to run if logical_2 and not logical_1
...
...
else:
Code to run if all previous logicals are false
Une forme simple qui revient souvent
if logical:
Code to run if logical true
ou
if logical:
Code to run if logical true
else:
Code to run if logical false
Un petit exemple
x = 5
if x<5:
x = x + 1
elif x>5:
x = x - 1
else:
x = x * 2
print(x)
for
¶Les boucles for
commencent par for
item dans iterable:, et la structure générique d’une boucle for
est
for item in iterable:
Code to run
item est un élément d'un objet itérable de Python. Les exemples les plus courants sont les range
, les listes, les tuples, les tableaux ou les matrices. La boucle for
parcourt tous les éléments de l'objet itérable, en commençant par l’élément $0$ et se poursuivant jusqu’à l’élément final. Lors de l'utilisation de tableaux multidimensionnels, seule la dimension extérieure est directement itérable. Par exemple, si $x$ est un tableau à $2$ dimensions, les éléments itérables sont $x[0]$, $x[1]$ et ainsi de suite.
import numpy as np
count = 0
for i in range(100):
count += i
count = 0
x = np.linspace(0,500,50)
for i in x:
count += i
count = 0
x = list(np.arange(-20,21))
for i in x:
count += i
La première boucle itérera sur $i = 0, 1, 2, \ldots,99$. La seconde boucle sur les valeurs produites par la fonction linspace
, qui renvoie un tableau avec $50$ points uniformément espacés compris entre $0$ et $500$ inclus. La dernière boucle sur $x$, un vecteur construit à partir d'un appel à liste(arange (-20,21))
, qui produit une liste contenant la séquence $-20, -19, \ldots, 0, \ldots, 19,20$. Les range
, tableaux et listes, sont itérables. La clé pour comprendre
Le comportement en boucle est le suivant: il effectue toujours une itération sur les éléments de l'iterable dans l'ordre dans lequel ils sont présentés (c'est-à-dire iterable[0]
, iterable[1]
,...).
Les boucles peuvent aussi être imbriquées
count = 0
for i in range(10):
for j in range(10):
count += j
ou peut contenir des variables de contrôle de flux
from numpy import random
returns = random.randn(100)
count = 0
for ret in returns:
if ret<0:
count += 1
Cette boucle for
peut être exprimée de manière équivalente en utilisant range
en tant qu'itérateur et len
pour obtenir le nombre d'éléments contenus dans l'objet itérable.
returns = np.random.randn(100)
count = 0
for i in range(len(returns)):
if returns[i]<0:
count += 1
print(count)
52
Enfin, ces idées peuvent être combinées pour produire des boucles imbriquées avec contrôle de flux.
import numpy as np
x = np.zeros((10,10))
for i in range(np.size(x,0)):
for j in range(np.size(x,1)):
if i<j:
x[i,j]=i+j;
else:
x[i,j]=i-j
print(x)
[[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9.] [ 1. 0. 3. 4. 5. 6. 7. 8. 9. 10.] [ 2. 1. 0. 5. 6. 7. 8. 9. 10. 11.] [ 3. 2. 1. 0. 7. 8. 9. 10. 11. 12.] [ 4. 3. 2. 1. 0. 9. 10. 11. 12. 13.] [ 5. 4. 3. 2. 1. 0. 11. 12. 13. 14.] [ 6. 5. 4. 3. 2. 1. 0. 13. 14. 15.] [ 7. 6. 5. 4. 3. 2. 1. 0. 15. 16.] [ 8. 7. 6. 5. 4. 3. 2. 1. 0. 17.] [ 9. 8. 7. 6. 5. 4. 3. 2. 1. 0.]]
ou des boucles contenant des boucles imbriquées exécutées en fonction d'une instruction de contrôle de flux.
x = np.zeros((10,10))
for i in range(np.size(x,0)):
if (i % 2) == 1:
for j in range(np.size(x,1)):
x[i,j] = i+j
else:
for j in range(int(i/2)):
x[i,j] = i-j
print(x)
[[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] [ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.] [ 2. 0. 0. 0. 0. 0. 0. 0. 0. 0.] [ 3. 4. 5. 6. 7. 8. 9. 10. 11. 12.] [ 4. 3. 0. 0. 0. 0. 0. 0. 0. 0.] [ 5. 6. 7. 8. 9. 10. 11. 12. 13. 14.] [ 6. 5. 4. 0. 0. 0. 0. 0. 0. 0.] [ 7. 8. 9. 10. 11. 12. 13. 14. 15. 16.] [ 8. 7. 6. 5. 0. 0. 0. 0. 0. 0.] [ 9. 10. 11. 12. 13. 14. 15. 16. 17. 18.]]
Important: La variable itérable ne doit pas être réaffectée une fois dans la boucle. Considérons, par exemple,
x = range(10)
for i in x:
print(i)
print('Length of x:', len(x))
x = range(5)
0 Length of x: 10 1 Length of x: 5 2 Length of x: 5 3 Length of x: 5 4 Length of x: 5 5 Length of x: 5 6 Length of x: 5 7 Length of x: 5 8 Length of x: 5 9 Length of x: 5
Il n’est pas prudent de modifier l'objet itérable lorsqu’on boucle dessus. Cela signifie que l'itérable ne doit pas changer de taille, ce qui peut se produire lors de l'utilisation d'une liste et des fonctions pop()
, insert()
ou append()
ou du mot clé del
. La boucle ci-dessous ne se terminera jamais (sauf pour l’instruction if
qui coupe la boucle) puisque $L$ est étendu à chaque itération.
L = [1, 2]
for i in L:
print(i)
L.append(i+2)
if i>5:
break
Enfin, les boucles for
peuvent être utilisées avec $2$ itérables encapsulés dans enumerate
, ce qui permet d’accéder directement aux éléments de l’itérable, ainsi que leur indice dans l’itéré.
x = np.linspace(0,100,11)
for i,y in enumerate(x):
print('i is :', i)
print('y is :', y)
Comme les blocs de contrôle de flux if...elif... else
, les boucles for
sont sensibles aux espaces. L'indentation de la ligne immédiatement en dessous de l'instruction for
détermine l'indentation que doivent avoir toutes les instructions du bloc.
Une boucle peut être terminée tôt en utilisant break
. break
est généralement utilisé après une instruction if
pour mettre fin à la
boucle prématurément si une condition est remplie.
x = randn(1000)
for i in x:
print(i)
if i > 2:
break
Etant donné que les boucles for
parcourent une valeur itérable de taille fixe, break
est généralement plus utile dans les boucles while
.
continue
peut être utilisé pour ignorer une itération d'une boucle et revenir immédiatement au début de la boucle en utilisant l'élément suivant dans iterable. continue
est couramment utilisé pour éviter un niveau d'imbrication, comme dans les deux exemples suivants
x = randn(10)
for i in x:
if i < 0:
print(i)
for i in x:
if i >= 0:
continue
print(i)
Éviter les niveaux excessifs d'indentation est essentiel dans la programmation Python. 4 espaces est généralement considéré comme le niveau maximum raisonnable. continue
est particulièrement utile dans la mesure où il peut être utilisé dans une boucle for
pour éviter un creux d'indentation.
Les boucles while
sont utiles lorsque le nombre d'itérations nécessaires dépend du résultat du contenu de la boucle. Lorsqu'une boucle ne doit s'arrêter que si certaines conditions sont remplies, par exemple lorsque la modification de certains paramètres est faible. La structure générique d'une boucle while
est
while logical:
Code to run
Update logical
Deux choses sont importantes lors de l'utilisation d'une boucle while
: premièrement, l'expression logical
doit être évaluée à vraie lorsque la boucle commence (ou la boucle sera ignorée) et deuxièmement, les entrées de l'expression logical
doivent être mises à jour dans la boucle. Si ce n'est pas le cas, la boucle continuera indéfiniment (appuyez sur CTRL + C pour interrompre une boucle interminable dans IPython).
count = 0
i = 1
while i<10:
count += i
i += 1
qui produit le même résultat que
count=0;
for i in range(0,10):
count += i
Les boucles while
doivent généralement être évitées lorsque les boucles for
suffisent. Cependant, il existe des situations où aucun équivalent de boucle for
n'existe.
# randn generates a standard normal random number
mu = abs(100*np.random.randn(1))
index = 1
while abs(mu) > .0001:
mu = (mu+np.random.randn(1))/index
index=index+1
Dans le bloc ci-dessus, le nombre d'itérations requis n'est pas connu à l'avance et puisque randn
est la réalisation d'une variable aléatoire normale, il peut prendre de nombreuses itérations jusqu'à ce que ce critère soit rempli.
break
¶break
peut être utilisé dans une boucle while
pour terminer immédiatement l'exécution. Normalement, break
ne doit pas être utilisé dans une boucle while
. Toutefois, break
peut être utilisé pour éviter d'exécuter du code en dessous de l'instruction break
, même si la condition logique est False
condition = True
i = 0
x = np.random.randn(1000000)
while condition:
if x[i] > 3.0:
break # No printing if x[i] > 3
print(x[i])
i += 1
-0.2579223691813828 -1.3773367175417117 -0.9853672723306478 -0.4766942251916157 0.021879575507100606 -1.0808856578871593 -0.40481262666455753 -0.3731211980817114 -0.8230777570791608 -1.5322336759371764 1.2916536962325096 0.4309613230576687 -1.5489256338047321 -0.038778140478520184 1.4100455046953282 0.04694046071301519 0.8236696793229623 -0.6036022054314778 -1.2018427858588794 -0.12355079834610437 0.27644693575828
Il est préférable de mettre à jour l’instruction logique qui détermine si la boucle while
doit être exécutée.
i = 0
while x[i] <= 3 and i < len(x):
print(x[i])
i += 1
-0.2579223691813828 -1.3773367175417117 -0.9853672723306478 -0.4766942251916157 0.021879575507100606 -1.0808856578871593 -0.40481262666455753 -0.3731211980817114 -0.8230777570791608 -1.5322336759371764 1.2916536962325096 0.4309613230576687 -1.5489256338047321 -0.038778140478520184 1.4100455046953282 0.04694046071301519 0.8236696793229623 -0.6036022054314778 -1.2018427858588794 -0.12355079834610437 0.27644693575828
continue
¶continue
peut être utilisé dans une boucle while
pour ignorer tout code restant dans la boucle et revenir immédiatement au début de la boucle, qui vérifie ensuite la condition while et exécute la boucle si elle est toujours vraie. Utiliser continue
lorsque la condition logique dans la boucle while
est False est identique à utiliser break
try ... except
¶La gestion des exceptions est une technique de programmation avancée qui peut être utilisée pour produire un code plus résistant (souvent au détriment de la vitesse).
Les blocs de code try ... except
sont utiles pour exécuter du code qui peut
échouer pour des raisons hors du contrôle du programmeur.
Dans la plupart des applications numériques, le code doit être déterministe et le code qui échoue peut généralement être évité. Par exemple, si vous ne pouvez pas lire les données d’une source de données qui n’est pas toujours disponible (par exemple, un site Web),
try ... except
peut être utilisé pour tenter d'exécuter le code, puis pour faire quelque chose si le code ne parvient pas à s'exécuter. La structure générique d'un bloc try ... except
est
try:
Dangerous Code
except ExceptionType1:
Code to run if ExceptionType1 is raised
except ExceptionType2:
Code to run if ExceptionType1 is raised
except:
Code to run if an unlisted exception type is raised
Un exemple simple de gestion des exceptions se produit lors de la tentative de conversion de texte en chiffres.
text = ('a','1','54.1','43.a')
for t in text:
try:
temp = float(t)
print(temp)
except ValueError:
print('Not convertable to a float')
Not convertable to a float 1.0 54.1 Not convertable to a float
La compréhension de liste est une méthode optimisée pour créer une liste qui peut simplifier le code lorsqu'un objet iterable est bouclé et que les résultats sont sauvegardés dans une liste, éventuellement conditionnée par un test logique. Une simple liste peut être utilisée pour convertir une boucle for
qui inclut un ajout en une seule ligne.
x = np.arange(5.0)
y = []
for i in range(len(x)):
y.append(np.exp(x[i]))
print(y)
z = [np.exp(x[i]) for i in range(len(x))]
print(z)
[1.0, 2.718281828459045, 7.38905609893065, 20.085536923187668, 54.598150033144236] [1.0, 2.718281828459045, 7.38905609893065, 20.085536923187668, 54.598150033144236]
On peut inclure une condition if
x = np.arange(5.0)
y = []
for i in range(len(x)):
if np.floor(i/2)==i/2:
y.append(x[i]**2)
print(y)
z = [x[i]**2 for i in range(len(x)) if floor(i/2)==i/2]
#print(z)
[0.0, 4.0, 16.0]
On peut aussi le faire avec des doubles boucles
x1 = np.arange(5.0)
x2 = np.arange(3.0)
y = []
for i in range(len(x1)):
for j in range(len(x2)):
y.append(x1[i]*x2[j])
print(y)
z = [x1[i]*x2[j] for i in range(len(x1)) for j in range(len(x2))]
print(z)
# Only when i==j
v = [x1[i]*x2[j] for i in range(len(x1)) for j in range(len(x2)) if i==j]
print(v)
[0.0, 0.0, 0.0, 0.0, 1.0, 2.0, 0.0, 2.0, 4.0, 0.0, 3.0, 6.0, 0.0, 4.0, 8.0] [0.0, 0.0, 0.0, 0.0, 1.0, 2.0, 0.0, 2.0, 4.0, 0.0, 3.0, 6.0, 0.0, 4.0, 8.0] [0.0, 1.0, 4.0]
import numpy as np
from math import*
x = np.arange(-5.0,5.0)
z_set = {x[i]**2.0 for i in range(len(x))}
print(z_set)
z_dict = {i:exp(i) for i in x}
print(z_dict)
z_tuple = tuple(i**3 for i in x)
print(z_tuple)
{0.0, 1.0, 4.0, 9.0, 16.0, 25.0} {-5.0: 0.006737946999085467, -4.0: 0.01831563888873418, -3.0: 0.049787068367863944, -2.0: 0.1353352832366127, -1.0: 0.36787944117144233, 0.0: 1.0, 1.0: 2.718281828459045, 2.0: 7.38905609893065, 3.0: 20.085536923187668, 4.0: 54.598150033144236} (-125.0, -64.0, -27.0, -8.0, -1.0, 0.0, 1.0, 8.0, 27.0, 64.0)
Trouver deux méthodes différentes pour utiliser une boucle for afin de remplir un tableau $5 \times 5$ avec $i \times j$, où $i$ est l'indice de la ligne et $j$ est l'indice de la colonne. L’une utilisera range
comme itérable, et l’autre devrait directement itérer sur
les lignes, puis les colonnes de la matrice.
Écrire une compréhension de liste qui itérera sur un tableau à une dimension et extraire les éléments négatifs dans une liste. Comment cela peut-il être fait en utilisant seulement des fonctions logiques (pas de boucle explicite), sans la compréhension de la liste (et en retournant un tableau)?
import numpy as np
x = np.random.randn(10000000)
%time y1 = [v for v in x if v < 0]
#print(y)
%time y2 = x[x<0]
CPU times: user 1.56 s, sys: 41.4 ms, total: 1.6 s Wall time: 1.6 s CPU times: user 43.7 ms, sys: 3.93 ms, total: 47.7 ms Wall time: 47.3 ms