Generator expressions

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.
Antworten
DKKA
User
Beiträge: 45
Registriert: Freitag 18. Oktober 2013, 14:20

Hallo,

Wie kann ich aus folgendem Code eine Generator Expression machen?

Code: Alles auswählen

z =((x**y) for x in [1,2,3] for y in [3,1,4] if x!=y)
for l in z:
    print l
für [1,2,3] ist klar:

Code: Alles auswählen

z =((x**y) for x in xrange(1,4) for y in [3,1,4] if x!=y)
for l in z:
    print l
Aber wie geht das für [3,1,4]???? Und wie kann ich am besten den belegten Speicher messen?

Danke!
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

DKKA hat geschrieben:Wie kann ich aus folgendem Code eine Generator Expression machen?
Du hast doch einen Generator-Ausdruck. Oder möchtest du so etwas?

Code: Alles auswählen

import math

def sample_data():
    for value in str(math.pi)[:4]:
        if value.isdigit():
            yield int(value)

z = ((x ** y) for x in range(1, 4) for y in sample_data() if x != y)
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Den Ausdruck für `z` halte ich noch für so gerade lesbar. Ab einer gewissen Komplexität würde ich sowas bevorzugt als Funktion mit `yield` ausdrücken, so wie es hier ja auch schon beispielhaft gezeigt wurde. Ansonsten versteht nämlich kaum noch jemand, was da genau passiert bzw erst nach angestrengtem Nachdenken. Und man muss die Dinge ja nicht ohne Not komplizierter machen als sie sind... ;)

Schließlich sind wir hier nicht bei Scala.
DKKA
User
Beiträge: 45
Registriert: Freitag 18. Oktober 2013, 14:20

/me hat geschrieben:
DKKA hat geschrieben:Wie kann ich aus folgendem Code eine Generator Expression machen?
Du hast doch einen Generator-Ausdruck. Oder möchtest du so etwas?

Code: Alles auswählen

import math

def sample_data():
    for value in str(math.pi)[:4]:
        if value.isdigit():
            yield int(value)

z = ((x ** y) for x in range(1, 4) for y in sample_data() if x != y)
Hm, daran habe ich auch gedacht. Also noch anstatt range -> xrange:

Code: Alles auswählen

z = ((x ** y) for x in xrange(1, 4) for y in sample_data() if x != y)
Oder verstehe ich hier was falsches? Wird so nicht weniger Speicher belegt?
Und gibt es in diesem Fall keine Generator-Methode in der Art von xrange, so dass man auf sample_data verzichten könnte??
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ja, es wird weniger Speicher belegt, da immer nur auf Abruf der aktuell auszugebende Wert im Speicher gehalten wird. Python gibt zwar nicht zwingend sofort den intern benutzten Speicher wieder frei, sobald ein Name innerhalb von Python nicht mehr gültig ist, aber das kann man in der Regel vernachlässigen. Und bei einem `range(1, 4)` spielt es ohnehin keine große Rolle, ob da jetzt eine Mini-Liste erzeugt wird, oder nicht.

Was meinst du mit `xrange()`-ähnlicher Funktion? Es kommt ja wohl darauf an, wie komplex die Berechnungen der Werte sein werden. Willst du jeweils eine zufällig vom System erzeugte Zahl haben, oder was stellst du dir genau vor?
BlackJack

@DKKA: Vielleicht verrätst Du mal was Du *eigentlich* vorhast. Denn bei diesen beiden kleinen Listen ist es vom Speicherverbrauch wahrscheinlich Wurst ob man da nun zwei Listen mit jeweils drei Elementen hat, oder eine Generatorfunktion erstellt, die ja auch Speicher belegt für den Zustand und so.

Wenn Du die Werte nicht generieren kannst, es also keine Vorschrift gibt sie effizient aufzuzählen wie zum Beispiel `xrange()` für aufsteigende Zahlen, dann musst Du sie im Speicher halten. Also Arbeitsspeicher oder langsamerer Hintergrundspeicher. Bei den verschachtelten Schleifen kommt noch hinzu das eine Wertefolge immer wieder durchgegangen werden muss, also die Werte für jeden Wert der anderen Folge aufgezählt, erzeugt, eingelesen, oder was auch immer werden müssen.

Wenn diese beiden Ausgangsfolgen den Arbeitsspeicher zu sprengen drohen stellt sich aber auch die Frage was Du eigentlich mit den generierten Werten von dem Generatorausdruck machen willst, denn die wirst Du dann niemals alle aufgezählt bekommen. Nicht in diesem Jahrhundert jedenfalls. ;-)

Vielleicht haben wir hier auch ein XY-Problem.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

DKKA hat geschrieben:Hm, daran habe ich auch gedacht. Also noch anstatt range -> xrange
Ich verwende inzwischen meistens Python 3 und da entspricht range dem xrange von Python 2.
DKKA hat geschrieben:Und gibt es in diesem Fall keine Generator-Methode in der Art von xrange, so dass man auf sample_data verzichten könnte??
Na ja, das vorher da gestanden habende [3, 1, 4] ist wohl die kürzeste und effektivste Methode um die Daten bereitzustellen. Im Endeffekt hängt eine sinnvolle Lösung absolut davon ab, welche Daten du da nun eigentlich haben willst.
DKKA
User
Beiträge: 45
Registriert: Freitag 18. Oktober 2013, 14:20

BlackJack hat geschrieben:@DKKA: Vielleicht verrätst Du mal was Du *eigentlich* vorhast. Denn bei diesen beiden kleinen Listen ist es vom Speicherverbrauch wahrscheinlich Wurst ob man da nun zwei Listen mit jeweils drei Elementen hat, oder eine Generatorfunktion erstellt, die ja auch Speicher belegt für den Zustand und so.

Wenn Du die Werte nicht generieren kannst, es also keine Vorschrift gibt sie effizient aufzuzählen wie zum Beispiel `xrange()` für aufsteigende Zahlen, dann musst Du sie im Speicher halten. Also Arbeitsspeicher oder langsamerer Hintergrundspeicher. Bei den verschachtelten Schleifen kommt noch hinzu das eine Wertefolge immer wieder durchgegangen werden muss, also die Werte für jeden Wert der anderen Folge aufgezählt, erzeugt, eingelesen, oder was auch immer werden müssen.

Wenn diese beiden Ausgangsfolgen den Arbeitsspeicher zu sprengen drohen stellt sich aber auch die Frage was Du eigentlich mit den generierten Werten von dem Generatorausdruck machen willst, denn die wirst Du dann niemals alle aufgezählt bekommen. Nicht in diesem Jahrhundert jedenfalls. ;-)

Vielleicht haben wir hier auch ein XY-Problem.
Ich möchte einen Generator mit allen Tupeln erstellen der mit jedem Element der Menga A und B eine Kombination macht. Ich suche ne Funktion die genauso funktioniert wie xrange, die aber nicht von bspw. 0-9 arbeitet sondern halt auch mit den Elementen [4,99,10] arbeiten kann.


Vielen Dank für die Hilfe!
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

DKKA hat geschrieben:Ich möchte einen Generator mit allen Tupeln erstellen der mit jedem Element der Menga A und B eine Kombination macht. Ich suche ne Funktion die genauso funktioniert wie xrange, die aber nicht von bspw. 0-9 arbeitet sondern halt auch mit den Elementen [4,99,10] arbeiten kann.
xrange funktioniert ungefähr so (Quelle wiki.python.org):

Code: Alles auswählen

def xrange(start, stop=None, step=1):
    if stop is None:
        stop = start
        start = 0
    else:
        stop = int(stop)
    start = int(start)
    step = int(step)

    while start < stop:
        yield start
        start += step
Die von mir in einem früheren Posting gezeigte Lösung mit yield macht doch genau das was du willst, wobei ich immer noch nicht verstehe warum das notwendig sein soll.

Ich habe immer noch den Verdacht, dass es auf Folgendes hinausläuft:

Code: Alles auswählen

import itertools

for element in itertools.product(range(1, 4), [3, 1, 4]):
    print(element)
BlackJack

@DKKA: Warum willst Du das? Erkläre doch mal bitte *was* Du machen willst und nicht *wie*.

Mit Listen mit drei Elementen macht das was Du machen willst keinen Sinn. Weder vom Speicherverbrauch noch von der Laufzeit her. Sollten es mehr Elemente sein, dann wird aus Deinen dreielementigen Listen nicht ersichtlich was die Berechnungsvorschrift für diese Elemente ist. Wenn Du die hast und in Code ausdrücken kannst, dann kannst Du eine Generatorfunktion schreiben. Oder noch besser ein Objekt welches das Sequenz-Protokoll implementiert, damit Du den Vorschlag von /me mit `product()` verwenden kannst.
DKKA
User
Beiträge: 45
Registriert: Freitag 18. Oktober 2013, 14:20

/me hat geschrieben:
DKKA hat geschrieben:Ich möchte einen Generator mit allen Tupeln erstellen der mit jedem Element der Menga A und B eine Kombination macht. Ich suche ne Funktion die genauso funktioniert wie xrange, die aber nicht von bspw. 0-9 arbeitet sondern halt auch mit den Elementen [4,99,10] arbeiten kann.
xrange funktioniert ungefähr so (Quelle wiki.python.org):

Code: Alles auswählen

def xrange(start, stop=None, step=1):
    if stop is None:
        stop = start
        start = 0
    else:
        stop = int(stop)
    start = int(start)
    step = int(step)

    while start < stop:
        yield start
        start += step
Die von mir in einem früheren Posting gezeigte Lösung mit yield macht doch genau das was du willst, wobei ich immer noch nicht verstehe warum das notwendig sein soll.

Ich habe immer noch den Verdacht, dass es auf Folgendes hinausläuft:

Code: Alles auswählen

import itertools

for element in itertools.product(range(1, 4), [3, 1, 4]):
    print(element)
Die von dir gezeigte Lösung mit yield ist auch genau das, was ich wollte. Ich dachte, es gab vielliecht noch einen anderen kürzeren Weg.

@BlackJack, natürlich ist anzunehmen, dass ich für dieses Beispiel hier kleine Mengen genommen habe, aber am Ende mit grösseren arbeite, sonst würde ich keine Generator-Expression benötigen.
Zuletzt geändert von DKKA am Mittwoch 27. November 2013, 10:43, insgesamt 1-mal geändert.
BlackJack

@DKKA: Aus der kleinen Menge liess sich aber nun so gar nicht ersehen wie die Elemente zustande kommen und nur wenn man das weiss kann man Deine Ausgangsfrage beantworten ob sich das als Generatorfunktion formulieren lässt. Oder gar als Sequenzdatentyp, denn dann könnte man es mit `itertools.product()` verwenden. Dazu muss man aber wissen ob und wie effizient sich ein Element aus dem vorherigen berechnen lässt, oder ob man gar ein Element (effizient) aus einer ganzen Zahl berechnen kann, die den Index in einer Sequenz beschreibt.

Und es bleibt auch immer noch die Frage ob sich das überhaupt lohnt in anbetracht der Tatsache das man die Elemente des Generatorausdrucks sehr wahrscheinlich gar nicht alle aufzählen kann wenn die Daten als Liste nicht in den Speicher passen.
Antworten