J'ai trouvé les différentes réponses très élégantes (en particulier celles d'Alex Martelli) mais je voulais quantifier les performances de première main, alors j'ai concocté le scénario suivant:
from itertools import repeat
N = 10000000
def payload(a):
pass
def standard(N):
for x in range(N):
payload(None)
def underscore(N):
for _ in range(N):
payload(None)
def loopiter(N):
for _ in repeat(None, N):
payload(None)
def loopiter2(N):
for _ in map(payload, repeat(None, N)):
pass
if __name__ == '__main__':
import timeit
print("standard: ",timeit.timeit("standard({})".format(N),
setup="from __main__ import standard", number=1))
print("underscore: ",timeit.timeit("underscore({})".format(N),
setup="from __main__ import underscore", number=1))
print("loopiter: ",timeit.timeit("loopiter({})".format(N),
setup="from __main__ import loopiter", number=1))
print("loopiter2: ",timeit.timeit("loopiter2({})".format(N),
setup="from __main__ import loopiter2", number=1))
J'ai également proposé une solution alternative qui s'appuie sur celle de Martelli et utilise map()
pour appeler la fonction de charge utile. OK, j'ai un peu triché en ce sens que j'ai pris la liberté de faire accepter à la charge utile un paramètre qui est rejeté: je ne sais pas s'il existe un moyen de contourner cela. Néanmoins, voici les résultats:
standard: 0.8398549720004667
underscore: 0.8413165839992871
loopiter: 0.7110594899968419
loopiter2: 0.5891903560004721
donc l'utilisation de la carte donne une amélioration d'environ 30% par rapport à la boucle standard pour et 19% supplémentaires par rapport à celle de Martelli.