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

Was erwartest Du? Der Generator liefert eine islice-Instanz zurück.
Ein Generator, der genau ein Element liefert ist auch nicht sehr sinnvoll.
Was ist der Sinn dahinter, diesen Generator 10 mal in einer Schleife zu erzeugen?

islice benutzt man wie einen Generator oder jeden anderen Iterator mit einer for-Schleife oder ähnlichem. Ich verstehe nicht, was Du hier noch erreichen willst.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Eigentlich wollte ich bei jedem Aufruf des Generators ein weiteres Element nach der vorbestimmten Reihenfolge aus der Liste `numbers[]` zurückbekommen.

Gruß
Atalanttore
Benutzeravatar
__blackjack__
User
Beiträge: 13061
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Atalanttore: Und das bekommst Du nicht? Und mal so nebenbei ist die Frage ob das ein Generator ist dafür ziemlich irrelevant weil hier im ganzen Thema bis her nur die Eigenschaften eines iterierbaren Objekts genutzt werden.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Jedesmal, wenn Du eine Generatorfunktion aufrufst, erhältst Du einen neuen Generator, von dem Du per next immer das einzige Element abfragst und das ist eben die islice-Instanz, so wie Du es programmiert hast.
Aber Du hast Dich da mit dem Generator verrannt, den brauchst Du nicht.

Code: Alles auswählen

from itertools import islice, cycle

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

def liste(l):
    return islice(cycle(l), 25)

for i in range(liste(numbers)):
    print(i)
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

__blackjack__ hat geschrieben: Samstag 11. August 2018, 20:48 @Atalanttore: Und das bekommst Du nicht? Und mal so nebenbei ist die Frage ob das ein Generator ist dafür ziemlich irrelevant weil hier im ganzen Thema bis her nur die Eigenschaften eines iterierbaren Objekts genutzt werden.
Im folgenden Beispiel wird auf die Anweisung, ob das Objekt ein `GeneratorType` ist, ein `True` zurückgegeben.

Code: Alles auswählen

numbers = []

for i in range(65):
    numbers.append(i)

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

print(isinstance(liste(numbers), types.GeneratorType))

Sirius3 hat geschrieben: Samstag 11. August 2018, 20:55

Code: Alles auswählen

from itertools import islice, cycle

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

def liste(l):
    return islice(cycle(l), 25)

for i in range(liste(numbers)):
    print(i)
Darauf erscheint bei mir leider diese Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "/home/ata/source/test2.py", line 8, in <module>
    for i in range(liste(numbers)):
TypeError: 'itertools.islice' object cannot be interpreted as an integer
Gruß
Atalanttore
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

War ja auch falsch:

Code: Alles auswählen

for i in liste(numbers):
    print(i)
Benutzeravatar
__blackjack__
User
Beiträge: 13061
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Atalanttore: Das ist ja schön das da True bei raus kommt und das ist ja auch richtig, aber das ändert nichts an meiner Bemerkung das die speziellen Eigenschaften eines Generators hier nirgends verwendet und damit auch nicht gebraucht werden. Hier wird nur iteriert und weder `send()`, `close()`, noch `throw()` aufgerufen. Solange man das nicht macht/braucht ist es Wurst ob man einen Generator oder ”nur” einen Iterator hat. Solange nicht `next()` selbst aufgerufen wird, sondern das ganze in einer ``for``-Schleife verarbeitet wird, braucht man nicht einmal einen Iterator, es reicht, dass das Objekt iterierbar ist, man also mit `iter()` einen Iterator von dem Objekt bekommen kann.

Dein Beispiel macht auch keinen Sinn.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Sirius3 hat geschrieben: Samstag 11. August 2018, 21:09 War ja auch falsch:

Code: Alles auswählen

for i in liste(numbers):
    print(i)
Damit funktioniert es:

Code: Alles auswählen

from itertools import islice, cycle

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

def liste(l):
    return islice(cycle(l), 25)

for i in liste(numbers):
    print(i)
Wird in der for-Schleife mit der Funktion `liste()` bei jeder Iteration der passende Rückgabewert zum Index `i` neu berechnet?

__blackjack__ hat geschrieben: Samstag 11. August 2018, 21:14 @Atalanttore: Das ist ja schön das da True bei raus kommt und das ist ja auch richtig, aber das ändert nichts an meiner Bemerkung das die speziellen Eigenschaften eines Generators hier nirgends verwendet und damit auch nicht gebraucht werden. Hier wird nur iteriert und weder `send()`, `close()`, noch `throw()` aufgerufen. Solange man das nicht macht/braucht ist es Wurst ob man einen Generator oder ”nur” einen Iterator hat.
`send()` und `throw()` habe ich noch nie benutzt/gebraucht und `close()` nur für das Schließen eines Dateiobjekts.

__blackjack__ hat geschrieben: Samstag 11. August 2018, 21:14 Solange nicht `next()` selbst aufgerufen wird, sondern das ganze in einer ``for``-Schleife verarbeitet wird, braucht man nicht einmal einen Iterator, es reicht, dass das Objekt iterierbar ist, man also mit `iter()` einen Iterator von dem Objekt bekommen kann.
Also mit `iter()` kann man einen Iterator (Zeiger?) vom Objekt (Liste?) erstellen. Bei jedem Zugriff mit `next()` auf diesen Iterator wird automatisch das Element, das auf das zuletzt zurückgegebene Element im Iterator folgt, zurückgegeben. Habe ich das so richtig verstanden?

Gruß
Atalanttore
Benutzeravatar
__blackjack__
User
Beiträge: 13061
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Atalanttore: Ob `cycle()` mit Indexwerten arbeitet, also Sequenztypen gesondert behandelt, wäre ein Implementierungsdetail. Das muss ja mit beliebigen iterierbaren Objekten funktionieren und nicht nur mit Sequenztypen wie Listen. Eine allgemeine Implementierung wäre also in zwei Phasen — in der ersten Phase werden die Elemente des Arguments zum Beispiel in einer Liste gespeichert *und* durchgereicht, und in der zweiten Phase wird entweder immer wieder über diese Liste iteriert. Ob nun mit Index oder über Iterator kann ja eigentlich egal sein. Wer's wissen möchte schaut halt in den Quelltext.

Ich wäre vorsichtig damit einen Iterator als Zeiger zu bezeichnen. Das Wort hat ja eine spezielle Bedeutung in der Programmierung und in dem Sinne ist ein Iterator kein Zeiger. Ein Iterator implementiert `__iter__()` — in der Regel liefert das Objekt dabei sich selbst zurück, und `__next__()` (Python 3) oder `next()` (Python 2). Letztere Methode liefert das nächste Element, oder löst eine `StopIteration` aus wenn es kein nächstes Element mehr gibt.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Ich habe das bestehende Beispiel auf die Verwendung eines Iterators umgebaut.

Code: Alles auswählen

from itertools import islice, cycle

numbers = []
for i in range(10):
    numbers.append(i)

iterator = iter(islice(cycle(numbers), 25))

for i in range(26):
    print(next(iterator))
Allerdings haben die Anweisungen `__iter__()` und `__next__()` bei mir (Python 3.6) nicht funktioniert und ich musste die Unterstriche weglassen.

Gruß
Atalanttore
Benutzeravatar
__blackjack__
User
Beiträge: 13061
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Atalanttore: Das macht keinen Sinn, warum machst Du das so umständlich? Jetzt hast Du einen zweiten Iterator und ein `i` das Du überhaupt nicht verwendest. Die erste Schleife ist auch unnötig kompliziert. Der Code sollte so aussehen:

Code: Alles auswählen

from itertools import islice, cycle

numbers = list(range(10))
for number in islice(cycle(numbers), 25):
    print(number)
`__iter__()` und `__next__()` sind die Methoden die das iterierbare Objekt und der Iterator haben müssen damit die Funktionen `iter()` und `next()` damit funktionieren. Also das iterierbare Objekt muss `__iter__()` implementieren und der Iterator beides.

(Das stimmt nicht so ganz, weil es noch andere Möglichkeiten gibt ein Objekt iterierbar zu machen, das hat aber eher historischen Wert.)
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
narpfel
User
Beiträge: 644
Registriert: Freitag 20. Oktober 2017, 16:10

Man muss noch nichtmal das `range`-Objekt in eine Liste umwandeln:

Code: Alles auswählen

from itertools import islice, cycle

for number in islice(cycle(range(10)), 25):
    print(number)
Benutzeravatar
__blackjack__
User
Beiträge: 13061
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Stimmt. :-)
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Dass islice schon ein Iterator ist, läßt sich leicht zeigen, indem man schaut, ob `iter` das selbe Element liefert

Code: Alles auswählen

from itertools import islice, cycle

iterator = islice(cycle(range(10)), 25)
print(iterator is iter(iterator))
# > True
Antworten