Sur la base de la réponse de @unutbu J'ai comparé les performances d'itération de deux listes identiques à l'aide de l'algorithme de Python 3.6. zip()
les fonctions de Python enumerate()
en utilisant un compteur manuel (voir count()
), en utilisant une liste d'index, et lors d'un scénario spécial où les éléments d'une des deux listes (soit foo
ou bar
) peut être utilisé pour indexer l'autre liste. Leurs performances pour l'impression et la création d'une nouvelle liste, respectivement, ont été étudiées à l'aide de la fonction timeit()
où le nombre de répétitions utilisées était de 1000 fois. L'un des scripts Python que j'avais créé pour effectuer ces recherches est donné ci-dessous. Les tailles des foo
et bar
Les listes variaient de 10 à 1 000 000 d'éléments.
Résultats :
-
A des fins d'impression : Les performances de toutes les approches considérées ont été observées comme étant approximativement similaires à celles de l'approche de la zip()
après avoir pris en compte une tolérance de précision de +/-5%. Une exception s'est produite lorsque la taille de la liste était inférieure à 100 éléments. Dans ce cas, la méthode de la liste d'index était légèrement plus lente que la méthode de la liste d'index. zip()
tandis que la fonction enumerate()
était ~9% plus rapide. Les autres méthodes ont donné des performances similaires à celles de la fonction zip()
fonction.
![Print loop 1000 reps]()
-
Pour créer des listes : Deux types d'approches de création de listes ont été explorés : l'utilisation de la (a) list.append()
et (b) compréhension de la liste . Après prise en compte d'une tolérance de précision de +/-5%, pour ces deux approches, la zip()
s'est avérée plus rapide que la fonction enumerate()
que l'utilisation d'un index de liste, que l'utilisation d'un compteur manuel. Le gain de performance par la zip()
dans ces comparaisons peut être de 5 à 60 % plus rapide. Il est intéressant de noter que l'utilisation de l'élément foo
à l'index bar
peut donner des performances équivalentes ou plus rapides (5 % à 20 %) que la zip()
fonction.
![Creating List - 1000reps]()
Donner un sens à ces résultats :
Un programmeur doit déterminer la quantité de temps de calcul par opération qui est significative ou qui a de l'importance.
Par exemple, à des fins d'impression, si ce critère de temps est de 1 seconde, c'est-à-dire 10**0 sec, alors en regardant l'axe des y du graphique qui est à gauche à 1 sec et en le projetant horizontalement jusqu'à ce qu'il atteigne les courbes monomiales, nous voyons que les listes dont la taille est supérieure à 144 éléments entraîneront un coût de calcul important et significatif pour le programmeur. En d'autres termes, toute performance gagnée par les approches mentionnées dans cette enquête pour les listes de plus petite taille sera insignifiante pour le programmeur. Le programmeur en conclura que les performances de l'approche zip()
pour itérer les instructions d'impression est similaire aux autres approches.
Conclusion
Des performances notables peuvent être obtenues en utilisant le zip()
pour itérer à travers deux listes en parallèle pendant list
création. Lorsque l'on itère à travers deux listes en parallèle pour imprimer les éléments des deux listes, la fonction zip()
donnera des performances similaires à celles de la fonction enumerate()
à l'utilisation d'une variable de compteur manuelle, à l'utilisation d'une liste d'index, et au scénario spécial où les éléments d'une des deux listes (soit foo
ou bar
) peut être utilisé pour indexer l'autre liste.
Le script de Python3.6 qui a été utilisé pour étudier la création de listes.
import timeit
import matplotlib.pyplot as plt
import numpy as np
def test_zip( foo, bar ):
store = []
for f, b in zip(foo, bar):
#print(f, b)
store.append( (f, b) )
def test_enumerate( foo, bar ):
store = []
for n, f in enumerate( foo ):
#print(f, bar[n])
store.append( (f, bar[n]) )
def test_count( foo, bar ):
store = []
count = 0
for f in foo:
#print(f, bar[count])
store.append( (f, bar[count]) )
count += 1
def test_indices( foo, bar, indices ):
store = []
for i in indices:
#print(foo[i], bar[i])
store.append( (foo[i], bar[i]) )
def test_existing_list_indices( foo, bar ):
store = []
for f in foo:
#print(f, bar[f])
store.append( (f, bar[f]) )
list_sizes = [ 10, 100, 1000, 10000, 100000, 1000000 ]
tz = []
te = []
tc = []
ti = []
tii= []
tcz = []
tce = []
tci = []
tcii= []
for a in list_sizes:
foo = [ i for i in range(a) ]
bar = [ i for i in range(a) ]
indices = [ i for i in range(a) ]
reps = 1000
tz.append( timeit.timeit( 'test_zip( foo, bar )',
'from __main__ import test_zip, foo, bar',
number=reps
)
)
te.append( timeit.timeit( 'test_enumerate( foo, bar )',
'from __main__ import test_enumerate, foo, bar',
number=reps
)
)
tc.append( timeit.timeit( 'test_count( foo, bar )',
'from __main__ import test_count, foo, bar',
number=reps
)
)
ti.append( timeit.timeit( 'test_indices( foo, bar, indices )',
'from __main__ import test_indices, foo, bar, indices',
number=reps
)
)
tii.append( timeit.timeit( 'test_existing_list_indices( foo, bar )',
'from __main__ import test_existing_list_indices, foo, bar',
number=reps
)
)
tcz.append( timeit.timeit( '[(f, b) for f, b in zip(foo, bar)]',
'from __main__ import foo, bar',
number=reps
)
)
tce.append( timeit.timeit( '[(f, bar[n]) for n, f in enumerate( foo )]',
'from __main__ import foo, bar',
number=reps
)
)
tci.append( timeit.timeit( '[(foo[i], bar[i]) for i in indices ]',
'from __main__ import foo, bar, indices',
number=reps
)
)
tcii.append( timeit.timeit( '[(f, bar[f]) for f in foo ]',
'from __main__ import foo, bar',
number=reps
)
)
print( f'te = {te}' )
print( f'ti = {ti}' )
print( f'tii = {tii}' )
print( f'tc = {tc}' )
print( f'tz = {tz}' )
print( f'tce = {te}' )
print( f'tci = {ti}' )
print( f'tcii = {tii}' )
print( f'tcz = {tz}' )
fig, ax = plt.subplots( 2, 2 )
ax[0,0].plot( list_sizes, te, label='enumerate()', marker='.' )
ax[0,0].plot( list_sizes, ti, label='index-list', marker='.' )
ax[0,0].plot( list_sizes, tii, label='element of foo', marker='.' )
ax[0,0].plot( list_sizes, tc, label='count()', marker='.' )
ax[0,0].plot( list_sizes, tz, label='zip()', marker='.')
ax[0,0].set_xscale('log')
ax[0,0].set_yscale('log')
ax[0,0].set_xlabel('List Size')
ax[0,0].set_ylabel('Time (s)')
ax[0,0].legend()
ax[0,0].grid( b=True, which='major', axis='both')
ax[0,0].grid( b=True, which='minor', axis='both')
ax[0,1].plot( list_sizes, np.array(te)/np.array(tz), label='enumerate()', marker='.' )
ax[0,1].plot( list_sizes, np.array(ti)/np.array(tz), label='index-list', marker='.' )
ax[0,1].plot( list_sizes, np.array(tii)/np.array(tz), label='element of foo', marker='.' )
ax[0,1].plot( list_sizes, np.array(tc)/np.array(tz), label='count()', marker='.' )
ax[0,1].set_xscale('log')
ax[0,1].set_xlabel('List Size')
ax[0,1].set_ylabel('Performances ( vs zip() function )')
ax[0,1].legend()
ax[0,1].grid( b=True, which='major', axis='both')
ax[0,1].grid( b=True, which='minor', axis='both')
ax[1,0].plot( list_sizes, tce, label='list comprehension using enumerate()', marker='.')
ax[1,0].plot( list_sizes, tci, label='list comprehension using index-list()', marker='.')
ax[1,0].plot( list_sizes, tcii, label='list comprehension using element of foo', marker='.')
ax[1,0].plot( list_sizes, tcz, label='list comprehension using zip()', marker='.')
ax[1,0].set_xscale('log')
ax[1,0].set_yscale('log')
ax[1,0].set_xlabel('List Size')
ax[1,0].set_ylabel('Time (s)')
ax[1,0].legend()
ax[1,0].grid( b=True, which='major', axis='both')
ax[1,0].grid( b=True, which='minor', axis='both')
ax[1,1].plot( list_sizes, np.array(tce)/np.array(tcz), label='enumerate()', marker='.' )
ax[1,1].plot( list_sizes, np.array(tci)/np.array(tcz), label='index-list', marker='.' )
ax[1,1].plot( list_sizes, np.array(tcii)/np.array(tcz), label='element of foo', marker='.' )
ax[1,1].set_xscale('log')
ax[1,1].set_xlabel('List Size')
ax[1,1].set_ylabel('Performances ( vs zip() function )')
ax[1,1].legend()
ax[1,1].grid( b=True, which='major', axis='both')
ax[1,1].grid( b=True, which='minor', axis='both')
plt.show()