Il existe dans beaucoup de langages (C/C++, JS…) une structure switch
(ou match
en Rust). Mais pas en Python. Trouvons le moyen le plus optimisé de la remplacer.
Ici, nous allons traduire ce code en Python:
for(int i=0; i<n; i++) { switch(i%4) { case 0: v = random()+1; break; case 1: v = random()+2; break; case 2: v = random()+3; break; default: v = random()+4; } }
Un tableau de comparaison des performances est disponible à la fin.
for i in range(n): v = 0 if i%4 == 0: v = random()+1 elif i%4 == 1: v = random()+2 elif i%4 == 2: v = random()+3 else: v = random()+4
Voilà l'approche naïve et redondante.
for i in range(n): v = { 0: random()+1, 1: random()+2, 2: random()+3 }.get(i, random()+4)
On utilise un dict
et sa méthode get
avec une valeur par défaut.
for i in range(n): try: v = { 0: random()+1, 1: random()+2, 2: random()+3 }[i] except KeyError: v = random()+4
Ce que beaucoup de gens disent sur les bonnes manières de programmer en Python, c'est qu'il est bon d'utiliser des try
sans perdre le temps de vérifier les données. Or, cet exemple montre que ce n'est pas toujours vrai.
t = [0,1,2] for i in range(n): if i in t: v = { 0: random()+1, 1: random()+2, 2: random()+3 }[i] else: v = random()+4
Alors on vérifie les données avant et on laisse tomber le try
. Au cas où le in
serait plus rapide avec une list
qu'avec un dict
, on essaie avec une list
.
t = [0,1,2] f = { 0: lambda: random()+1, 1: lambda: random()+2, 2: lambda: random()+3 } for i in range(n): if i in t: v = f[i]() else: v = random()+4
Ne redéclarons pas notre dict
à chaque itération grâce aux lambda
.
f = { 0: lambda: random()+1, 1: lambda: random()+2, 2: lambda: random()+3 } for i in range(n): if i in f: v = f[i]() else: v = random()+4
Et finalement on peut abandonner la list
.
Les durées sont donnés en secondes pour n = 50,000,000 (Linux 4.19, x86_64, Intel Core i5 g7, GCC 8.2.1).
Solution | Python2.7.16 | Python3.7.3 |
---|---|---|
1 | 8.442 | 7.273 |
2 | 20.957 | 19.701 |
3 | 38.195 | 27.798 |
4 | 5.760 | 5.505 |
5 | 5.793 | 5.495 |
6 | 5.090 | 4.620 |
La solution la plus rapide est donc un dict
avec des lambda
, dans un if
. Le try
/except
est de loin le pire, et la série de if
ne donne pas de très bons résultats. Au passage, on peut constater de meilleures performances avec Python3 par rapport à Python2.