Seite 1 von 2

Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Sonntag 1. Juli 2018, 16:28
von Atalanttore
Hallo

Ich würde gerne die Elemente einer Liste mit 12 Einträgen immer wieder durchlaufen. Sobald das Ende der Liste erreicht ist, sollte der Durchlauf wieder von vorne beginnen, aber insgesamt sollen nur 1000 Werte ausgegeben werden. Die Liste müsste also 83 mal komplett durchlaufen und beim 84. Durchlauf beim 4. Element abgebrochen werden.

Bietet Python eine passende Funktion dafür an?

Gruß
Atalanttore

Re: Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Sonntag 1. Juli 2018, 16:33
von Sirius3
Aus dem Modul itertools, cycle und islice.

Re: Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Sonntag 1. Juli 2018, 17:07
von nezzcarth
Eine mögliche Variante:

Code: Alles auswählen

In [1]: from itertools import cycle

In [2]: def repeat(items, limit):
   ...:     items = cycle(items)
   ...:     for i in range(limit):
   ...:         yield next(items)
   ...:         

In [3]: l = [1,2,3]

In [4]: list(repeat(l, 5))
Out[4]: [1, 2, 3, 1, 2]

Re: Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Sonntag 1. Juli 2018, 17:12
von __blackjack__
@nezzcarth: Aber es gibt doch `islice()`, warum programmierst Du das noch mal in abgespeckter Form nach und benennst das dann auch noch wie eine andere Funktion aus `itertools` die etwas anderes macht?

Code: Alles auswählen

In [174]: l = [1,2,3]

In [175]: list(islice(cycle(l), 5))
Out[175]: [1, 2, 3, 1, 2]

Re: Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Sonntag 1. Juli 2018, 17:23
von ThomasL
Eine interessante Variante kann man mit einem Generator bauen:
Edit: meine Eingabe hat sich mit obigen Antworten überschnitten, hier ein schöner handgestrickter Generator, ohne import schnickschnack

Code: Alles auswählen

def my_generator(my_list, count):
    counter = count + 1
    while counter > 0:
        for item in my_list:
            counter -= 1
            if counter > 0:
                yield item

output = my_generator([1,2,3,4,5,6,7,8,9,10,11,12], 1000)

for i in range(1000):
    print(f'{next(output)} ', end='')

Re: Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Sonntag 1. Juli 2018, 17:27
von nezzcarth
@__blackjack__: Offensichtlich, weil ich es nicht besser wusste ;) (an islice habe ich nicht gedacht und außer cycle oder repeat wüsste ich nicht, wie man es groß anders nennen soll; repeat wird ja zumindest nicht verwendet).

Re: Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Sonntag 1. Juli 2018, 17:34
von ThomasL
__blackjack__ hat geschrieben: Sonntag 1. Juli 2018, 17:12

Code: Alles auswählen

In [174]: l = [1,2,3]
In [175]: list(islice(cycle(l), 5))
Out[175]: [1, 2, 3, 1, 2]
Verstehe ich diese Lösung so, dass hier eine Liste mit 1000 Elementen erzeugt wird, über die man dann iterieren muss?
Wenn ja, nicht sehr ökonomisch.

Re: Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Sonntag 1. Juli 2018, 17:36
von nezzcarth
islice gibt einen Iterator zurück. Die Umwandlung in eine Liste war zumindest in meinem Beispiel (das __blackjack__ verbessert hat) nur zur Illustration gedacht; eine Schleife mit einem Dummy-Print nimmt mehr Platz weg.

Re: Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Sonntag 1. Juli 2018, 17:54
von Sirius3
@ThomasL: Import-Schnickschnack ist genau dafür da, dass man nicht jeden Bling selbst schreiben muß. Nicht sehr ökonomisch ist an Deiner Lösung, dass die Liste immer komplett durchlaufen wird. Schönheit definiert ja jeder anders, z.B. die for-range-Schleife.

Re: Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Sonntag 1. Juli 2018, 18:20
von Atalanttore
Danke für die vielen Antworten.

Gruß
Atalanttore

Re: Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Sonntag 1. Juli 2018, 18:22
von ThomasL
@Sirius3 Du hast die ursprüngliche Fragestellung gelesen?
Atalanttore hat geschrieben: Sonntag 1. Juli 2018, 16:28 Hallo, Ich würde gerne die Elemente einer Liste mit 12 Einträgen immer wieder durchlaufen. Sobald das Ende der Liste erreicht ist, sollte der Durchlauf wieder von vorne beginnen, aber insgesamt sollen nur 1000 Werte ausgegeben werden. Die Liste müsste also 83 mal komplett durchlaufen und beim 84. Durchlauf beim 4. Element abgebrochen werden.
Bietet Python eine passende Funktion dafür an? Gruß Atalanttore
Was stört dich jetzt bitte an meinem Beispiel Ausgabecode, dass er alle Elemente ausgibt? Hä??

Ich habe einen konfigurierbaren Generator, die Liste die immer wieder durchlaufen werden soll
und die Anzahl der Werte die ausgegeben werden sollen können angegeben werden.

Die for-schleife ist nur ein Beispiel um zu zeigen, wie man jetzt die einzelnen Werte (in dem Beispiel mit next() ) abruft.

Selbstverständlich ist diese Lösung hier eleganter:

Code: Alles auswählen

for item in output:
    print(f'{item} ', end='')

Re: Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Sonntag 1. Juli 2018, 18:28
von kbr
@ThomasL: Wenn es Dir um Ökonomie geht, dann nimm:

Code: Alles auswählen

for n in islice(cycle(range(12), 1000):
    ...
Sicher sind eigene Generatoren schön, aber noch schöner ist es, wenn man sich bereits der builtins und aus der Standard-Library bedienen kann.

Re: Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Sonntag 1. Juli 2018, 19:19
von __blackjack__
@ThomasL: Bei Deiner Lösung stört, dass die Liste auch in den Fällen komplett durchlaufen wird wo gar nicht alle Elemente benötigt werden. Wenn man 1000 Elemente übergibt und 1005 haben möchte, wird die ``for``-Schleife *in* der Funktion 2000 mal durchlaufen. Das sind 1995 mal mehr als nötig wären. Klar kann man das jetzt fixen — man kann aber auch einfach `cycle()` und `islice()` nehmen. Die Funktionen in `itertools` sind ja genau dafür gemacht das man die benutzen und kombinieren kann/soll, um das Rad nicht immer wieder neu zu erfinden, und dann nicht ganz rund sondern mit Ecken und Kanten und wenn man Pech hat auch mit Sollbruchstellen. `cycle()` kann zudem nicht nur mit Sequenztypen, sondern mit allen iterierbaren Objekten.

Re: Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Sonntag 1. Juli 2018, 19:51
von ThomasL
__blackjack__ hat geschrieben: Sonntag 1. Juli 2018, 19:19 @ThomasL: Bei Deiner Lösung stört, dass die Liste auch in den Fällen komplett durchlaufen wird wo gar nicht alle Elemente benötigt werden.
Sorry aber dies kann ich nicht nachvollziehen, du weißt was ein Generator ist?
Bei jedem Abruf durch next() oder bei for item in wird EIN Element zurück gegeben.
Wenn ich keine Elemente abrufe wird auch nichts durchlaufen
Wenn man 1000 Elemente übergibt und 1005 haben möchte
Was ist denn das für nen Quatsch.
Wenn man 1005 Elemente haben möchte, dann übergibt man den Wert 1005 und nicht 1000.
Und es werden auch keine 1000 Elemente übergeben. Hast du die Generatorfunktion my_generator überhaupt verstanden?
Der Funktion wird die Liste deren einzelne Elemente nacheinander und nach kompletten Durchlauf wieder von vorne an
ausgegeben werden sollen und die Anzahl der Elemente die maximale ausgegeben werden sollen, übergeben.
wird die ``for``-Schleife *in* der Funktion 2000 mal durchlaufen. Das sind 1995 mal mehr als nötig wären.
auch diesen Murks musst du mir mal genauer erklären.
Die for-Schleife iteriert über die Elemente der übergebenen Liste, und mehr nicht.
Lass diesen Code hier laufen und dann überlege dir eine Erklärung

Code: Alles auswählen

def my_generator(my_list, count):
    counter = count + 1
    while counter > 0:
        print(f'Counter:{counter}')  # eingefügt
        for item in my_list:
            counter -= 1
            if counter > 0:
                yield item

output = my_generator([1,2,3,4,5,6,7,8,9,10,11,12], 1000)

for item in output:
    print(f'Item:{item}')

Re: Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Sonntag 1. Juli 2018, 20:06
von __blackjack__
@ThomasL: Ja, ich weiss was ein Generator ist. Natürlich wird nichts durchlaufen wenn man keine Elemente abfragt, aber es werden ja Elemente abgefragt, sonst wäre es sinnlos den Generator zu erstellen.

Wieso ist es Quatsch 1000 Elemente zu übergeben und 1005 zurückbekommen zu wollen? Genau darum geht es doch. Original halt nicht 1000 und 1005 sondern es werden 12 Elemente übergenen und man möchte 1000 haben. Also statt ``my_generator(range(12), 1000)`` im Original war mein Beispiel ``my_generator(range(1000), 1005)``.

Den Code muss ich nicht laufen lassen und auch keine Erklärung finden, denn bei *der* Ausgabe sieht man das Problem nicht, denn die zeigt *nicht* wie oft die ``for``-Schleife durchlaufen wird. Wenn der Code das zeigen würde, dann hättest Du das Problem ja verstanden und würdest nicht mit Worten wie Murks und Quatsch in *meine* Richtung werfen. ;-)

Lass *Du* doch mal *das* hier laufen, wo in jedem Durchlauf der ``for``-Schleife ein Sternchen ausgegeben wird, und erkläre mir warum da 10 statt 6 Sternchen ausgegeben werden. Bei meinem Beispiel wären es dann 2000 statt 1005.

Code: Alles auswählen

def my_generator(my_list, count):
    counter = count + 1
    while counter > 0:
        for item in my_list:
            print('*') # eingefügt
            counter -= 1
            if counter > 0:
                yield item

for item in my_generator(range(5), 6):
    print(item)
Ausgabe:

Code: Alles auswählen

*
0
*
1
*
2
*
3
*
4
*
0
*
*
*
*
Da sind am Ende mindestens drei Sternchen zu viel.

Re: Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Sonntag 1. Juli 2018, 20:43
von ThomasL
__blackjack__ hat geschrieben: Sonntag 1. Juli 2018, 20:06 Wieso ist es Quatsch 1000 Elemente zu übergeben und 1005 zurückbekommen zu wollen?
Also statt ``my_generator(range(12), 1000)`` im Original war mein Beispiel ``my_generator(range(1000), 1005)``.
Mit diesem Beispiel habe ich verstanden, was du gemeint hast.
Ich hatte es so missverstanden, das my_generator(liste, 1000) erzeugt wird und dann 1005 Elemente ausgegeben werden sollen
Den Code muss ich nicht laufen lassen und auch keine Erklärung finden, denn bei *der* Ausgabe sieht man das Problem nicht, denn die zeigt *nicht* wie oft die ``for``-Schleife durchlaufen wird. Wenn der Code das zeigen würde, dann hättest Du das Problem ja verstanden und würdest nicht mit Worten wie Murks und Quatsch in *meine* Richtung werfen. ;-)
Stimmt, ich nehme das Murks und Quatsch peinlich berührt zurück und entschuldige mich. :!:
Da war ich ja wohl komplett auf dem Holzweg.
und erkläre mir warum da 10 statt 6 Sternchen ausgegeben werden. Bei meinem Beispiel wären es dann 2000 statt 1005.
weil das hier gefehlt hat

Code: Alles auswählen

            if counter > 0:
                yield item
            else:
                break
jetzt kommt nur noch ein einziges Sternchen extra :shock:

Re: Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Samstag 4. August 2018, 20:35
von Atalanttore
__blackjack__ hat geschrieben: Sonntag 1. Juli 2018, 17:12

Code: Alles auswählen

In [174]: l = [1,2,3]

In [175]: list(islice(cycle(l), 5))
Out[175]: [1, 2, 3, 1, 2]
Ich bin gerade dabei meine Übungsprojekte zu optimieren.

Anstatt eine lange Liste mit Zahlen zu erzeugen und zu speichern, wollte ich dafür einen Generator verwenden.

Allerdings ist mir nicht so ganz klar, ob die Funktionen `cycle` und `islice` aus dem Modul `itertools` bereits wie ein Generator funktionieren.

Gruß
Atalanttore

Re: Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Samstag 11. August 2018, 19:12
von Atalanttore
Mit folgendem Code, erstellt mit Unterstützung von Stack Overflow, habe ich festgestellt, dass es kein Generator ist.

Code: Alles auswählen

from itertools import islice, cycle
import types

l = [1,2,3]
print(isinstance(list(islice(cycle(l), 5)), types.GeneratorType))
Gruß
Atalanttore

Re: Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Samstag 11. August 2018, 19:22
von Sirius3
Du wandelst das islice-Objekt ja auch in eine Liste um. Ein Generator ist auch nur eine von vielen Möglichkeiten von einem iterierbaren Objekt. Da islice in C implementiert ist, ist es kein Generator.
Bei `cycle` ist eigentlich klar, dass es nicht wie eine Liste ist, weil es unendlich viele Elemente enthält.

Re: Liste wieder und wieder durchlaufen bis die Anzahl der zurückgegebenen Elemente stimmt

Verfasst: Samstag 11. August 2018, 20:09
von Atalanttore
Sirius3 hat geschrieben: Samstag 11. August 2018, 19:22 Du wandelst das islice-Objekt ja auch in eine Liste um. Ein Generator ist auch nur eine von vielen Möglichkeiten von einem iterierbaren Objekt.
Ich habe das Beispiel erweitert und die Umwandlung in eine Liste entfernt.

Code: Alles auswählen

from itertools import islice, cycle
import types

numbers = [1,2,3,4,5,6,7,8,9,10]

def liste(l):
    number = islice(cycle(l), 25)
    yield number

for i in range(10):
    print(next(liste(numbers)))

print(isinstance(liste(numbers), types.GeneratorType))
Damit wird als `GeneratorType` ein `True` zurückgegeben, aber als Wert für `number` nur noch `<itertools.islice object at 0x7f70ad7cc0e8>`.

Sirius3 hat geschrieben: Samstag 11. August 2018, 19:22 Da islice in C implementiert ist, ist es kein Generator.
Warum ist das so?

Sirius3 hat geschrieben: Samstag 11. August 2018, 19:22 Bei `cycle` ist eigentlich klar, dass es nicht wie eine Liste ist, weil es unendlich viele Elemente enthält.
Soweit ich es jetzt verstanden habe, wiederholt `cycle()` die Elemente in der übergebenen Liste wieder und wieder. `islice()` sorgt dann dafür, dass bei der übergebenen Anzahl an Elementen Schluss ist.


Gruß
Atalanttore