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

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

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
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Aus dem Modul itertools, cycle und islice.
nezzcarth
User
Beiträge: 1632
Registriert: Samstag 16. April 2011, 12:47

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]
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@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]
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

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='')
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
nezzcarth
User
Beiträge: 1632
Registriert: Samstag 16. April 2011, 12:47

@__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).
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

__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.
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
nezzcarth
User
Beiträge: 1632
Registriert: Samstag 16. April 2011, 12:47

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.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@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.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Danke für die vielen Antworten.

Gruß
Atalanttore
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

@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='')
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@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.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@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.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

__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}')
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@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.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

__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:
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

__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
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

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
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

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.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

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
Antworten