Wie kann ich eine Liste von Elementen aus einem Iterator erstellen (den Iterator in eine Liste umwandeln)?

Lesezeit: 5 Minuten

Benutzeravatar von systempuntoout
systempuntoout

Gegeben ein Iterator user_iteratorwie kann ich über den Iterator eine Liste der ausgegebenen Objekte iterieren?

Ich habe diesen Code, der zu funktionieren scheint:

user_list = [user for user in user_iterator]

Aber gibt es etwas schnelleres, besseres oder korrekteres?

  • Stellen Sie vor der Optimierung sicher, dass Sie einige Profile erstellt haben, um zu beweisen, dass dies wirklich der Engpass ist.

    – S. Lott

    24. September 2010 um 20:52 Uhr

  • @S.Lott. Normalerweise stimme ich dieser Einstellung zu, aber in diesem Fall sollte es sehr stilistisch optimiert werden, was es, wie so oft bei Python, auch auf Geschwindigkeit optimiert.

    – Aaronasterling

    24. September 2010 um 21:10 Uhr


  • Das OP sagte nichts über einen Engpass. Es ist eine völlig in Ordnung allgemeine Frage mit einer einfachen Antwort, es muss nicht von einer bestimmten Anwendung abhängen, die über einen Profiler ausgeführt werden kann.

    – Ken Williams

    19. März 2017 um 3:49 Uhr


  • Der kompakteste Weg ist [*iterator].

    – Herausforderer5

    27. März 2017 um 6:04 Uhr

Benutzeravatar von Mikerobi
Mikerobi

list(your_iterator)

  • Eigentlich fast immer um einiges schneller. Auch viel offensichtlicher.

    – Thomas Wouters

    24. September 2010 um 20:52 Uhr


  • @systempuntoout Es läuft vollständig in C. Das Listenverständnis ist in Python. Läuft natürlich schneller.

    – Aaronasterling

    24. September 2010 um 22:24 Uhr

  • Ich hasse es immer noch total, dass es in Python keinen besseren Weg gibt. Es ist mühsam, beide Seiten eines Ausdrucks bearbeiten zu müssen, nur um ihn segmentieren oder indizieren zu können. (sehr häufig in python3, wenn es sich um einen reinen Ausdruck wie zip oder eine Karte mit einer reinen Funktion handelt)

    – Jo So

    24. Oktober 2015 um 5:29 Uhr


  • In meinem Schnelltest [*your_iterator] schien etwa doppelt so schnell zu sein wie list(your_iterator). Stimmt das generell oder war es nur ein bestimmter Anlass? (Ich habe eine map als Iterator.)

    – Neinstein

    10. September 2018 um 21:49 Uhr

  • @Bachsau: Zugegeben, es ist ziemlich gut, aber vergleichen Sie es mit Bash-Skripten, bei denen Sie die aktuelle Ausgabe manipulieren können, indem Sie eine Pipe und einen weiteren Filterbefehl genau rechts vom aktuellen Befehl anhängen. Es ist ärgerlich, dass Sie für eine so kleine Unterscheidung (Iterator vs. materialisierte Liste) oft den Cursor zurückbewegen müssen.

    – Jo So

    25. September 2019 um 13:19 Uhr

Benutzeravatar von kederrac
Kederrac

seit Python 3.5 Sie können verwenden * Iterierbarer Entpackoperator:

user_list = [*your_iterator]

aber der pythonische Weg, es zu tun, ist:

user_list  = list(your_iterator)

  • Bitte posten Sie die Ergebnisse eines Geschwindigkeitstests für mehr Punkte!

    – Robino

    15. Oktober 2020 um 8:34 Uhr

  • @Robino danke für den Vorschlag, Sie können stackoverflow.com/a/64512225/3161575 überprüfen

    – kederrac

    24. Oktober 2020 um 10:12 Uhr

Benutzeravatar von kederrac
Kederrac

@Robino schlug vor, einige sinnvolle Tests hinzuzufügen, also ist hier ein einfacher Benchmark zwischen 3 möglichen Wegen (vielleicht die am häufigsten verwendeten), um einen Iterator in eine Liste umzuwandeln:

  1. nach Typkonstruktor

    list(my_iterator)

  2. durch Auspacken

    [*my_iterator]

  3. Listenverständnis verwenden

    [e for e in my_iterator]

Ich habe verwendet simple_bechmark Bibliothek:

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()

Geben Sie hier die Bildbeschreibung ein

Wie Sie sehen können, ist es sehr schwierig, zwischen der Konvertierung durch den Konstruktor und der Konvertierung durch Entpacken zu unterscheiden, die Konvertierung durch Listenverständnis ist der „langsamste“ Ansatz.


Ich habe auch verschiedene Python-Versionen (3.6, 3.7, 3.8, 3.9) getestet, indem ich das folgende einfache Skript verwendet habe:

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))

Das Skript wird über einen Unterprozess von einem Jupyter Notebook (oder einem Skript) ausgeführt, der Größenparameter wird über Befehlszeilenargumente übergeben und die Skriptergebnisse werden der Standardausgabe entnommen.

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_

Geben Sie hier die Bildbeschreibung ein

Sie können mein vollständiges Notizbuch von erhalten Hier.

In den meisten Fällen, in meinen Tests, zeigt sich, dass das Entpacken schneller ist, aber der Unterschied ist so gering, dass sich die Ergebnisse von einem Lauf zum anderen ändern können. Auch hier ist der Verständnisansatz am langsamsten, tatsächlich sind die anderen 2 Methoden bis zu ~ 60% schneller.

1448680cookie-checkWie kann ich eine Liste von Elementen aus einem Iterator erstellen (den Iterator in eine Liste umwandeln)?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy