Avoir un iterator
objet, y a-t-il quelque chose de plus rapide, meilleur ou plus correct qu'une compréhension de liste pour obtenir une liste des objets retournés par l'itérateur?
user_list = [user for user in user_iterator]
Avoir un iterator
objet, y a-t-il quelque chose de plus rapide, meilleur ou plus correct qu'une compréhension de liste pour obtenir une liste des objets retournés par l'itérateur?
user_list = [user for user in user_iterator]
[*iterator]
.
Réponses:
list(your_iterator)
[*your_iterator]
semblé être environ deux fois plus rapide que list(your_iterator)
. Est-ce généralement vrai ou était-ce juste une occasion spécifique? (J'ai utilisé un map
itérateur.)
depuis python 3.5, vous pouvez utiliser l' *
opérateur de décompression itérable:
user_list = [*your_iterator]
mais la manière pythonique de le faire est:
user_list = list(your_iterator)
@Robino suggérait d'ajouter quelques tests qui ont du sens, voici donc un simple benchmark entre 3 façons possibles (peut-être les plus utilisées) de convertir un itérateur en liste:
list(my_iterator)
[*my_iterator]
[e for e in my_iterator]
J'utilise simple_bechmark bibliothèque
from simple_benchmark import BenchmarkBuilder
from heapq import nsmallest
b = BenchmarkBuilder()
@b.add_function()
def convert_by_type_constructor(size):
list(iter(range(size)))
@b.add_function()
def convert_by_list_comprehension(size):
[e for e in iter(range(size))]
@b.add_function()
def convert_by_unpacking(size):
[*iter(range(size))]
@b.add_arguments('Convert an iterator to a list')
def argument_provider():
for exp in range(2, 22):
size = 2**exp
yield size, size
r = b.run()
r.plot()
Comme vous pouvez le voir, il est très difficile de faire une différence entre la conversion par le constructeur et la conversion par décompression, la conversion par compréhension de liste est l'approche la plus «lente».
J'ai également testé différentes versions de Python (3.6, 3.7, 3.8, 3.9) en utilisant le script simple suivant:
import argparse
import timeit
parser = argparse.ArgumentParser(
description='Test convert iterator to list')
parser.add_argument(
'--size', help='The number of elements from iterator')
args = parser.parse_args()
size = int(args.size)
repeat_number = 10000
# do not wait too much if the size is too big
if size > 10000:
repeat_number = 100
def test_convert_by_type_constructor():
list(iter(range(size)))
def test_convert_by_list_comprehension():
[e for e in iter(range(size))]
def test_convert_by_unpacking():
[*iter(range(size))]
def get_avg_time_in_ms(func):
avg_time = timeit.timeit(func, number=repeat_number) * 1000 / repeat_number
return round(avg_time, 6)
funcs = [test_convert_by_type_constructor,
test_convert_by_unpacking, test_convert_by_list_comprehension]
print(*map(get_avg_time_in_ms, funcs))
Le script sera exécuté via un sous-processus à partir d'un Jupyter Notebook (ou d'un script), le paramètre size sera passé via des arguments de ligne de commande et les résultats du script seront extraits de la sortie standard.
from subprocess import PIPE, run
import pandas
simple_data = {'constructor': [], 'unpacking': [], 'comprehension': [],
'size': [], 'python version': []}
size_test = 100, 1000, 10_000, 100_000, 1_000_000
for version in ['3.6', '3.7', '3.8', '3.9']:
print('test for python', version)
for size in size_test:
command = [f'python{version}', 'perf_test_convert_iterator.py', f'--size={size}']
result = run(command, stdout=PIPE, stderr=PIPE, universal_newlines=True)
constructor, unpacking, comprehension = result.stdout.split()
simple_data['constructor'].append(float(constructor))
simple_data['unpacking'].append(float(unpacking))
simple_data['comprehension'].append(float(comprehension))
simple_data['python version'].append(version)
simple_data['size'].append(size)
df_ = pandas.DataFrame(simple_data)
df_
Vous pouvez obtenir mon cahier complet d' ici .
Dans la plupart des cas, dans mes tests, le déballage s'avère plus rapide, mais la différence est si petite que les résultats peuvent changer d'une exécution à l'autre. Encore une fois, l'approche de compréhension est la plus lente, en fait, les 2 autres méthodes sont jusqu'à ~ 60% plus rapides.