Generatoren & Iteratoren

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
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

Hallo Freunde,

da ich noch relativ neu auf Python-Gebiet bin habe ich folgende Frage.
Ich habe eine eigene Linkliste entworfen und ich stehe nun vor der Problematik, wie ich am geschicktesten die Iteratoren unterbringe. Da wäre zum einen die __iter__() Methode die ich in die Klasse integriere. Das funzt ganz gut nur mit der Einschränkung, dass ich nur GoF iterieren kann. Gibt es hier eine Möglichkeit das anders zu gestallten? z.B. rückwärts iterieren?

Als alternative habe ich ein seperates Modul mit einer vielzahl - so wie ich mir das auch ohnehin vorgestellt habe - an Funktionen.

forwärts/rückwärts iterieren
mit/ohne prefetch

usw.

Was ist jetzt nun am elegantesten?

Viele Grüße,

Chris
be or not to be
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

Zu wenig Input.
Woher kommen die Links? SQL?
Wie hast du __iter__ implementiert?
Bottle: Micro Web Framework + Development Blog
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

hallo Defnull,

Woher kommen die Links? SQL?
welche links?? SQL ???
ich meine verkettete Listen...

n1 n2 n3
p <- p <- p
n -> n -> n
Wie hast du __iter__ implementiert

Code: Alles auswählen


def __iter__(self):
 self.current = self.head
 return self

def next(self):
 if self.current == None: 
  raise StopIter....
 else:
  self.current = self.current.next
  return self.current

vg chris
be or not to be
Benutzeravatar
b.esser-wisser
User
Beiträge: 272
Registriert: Freitag 20. Februar 2009, 14:21
Wohnort: Bundeshauptstadt B.

Benutz' doch ein extra Iterator-objekt:

Code: Alles auswählen

class Iterator(object):
    def __init__(self, node, forward=True):
        self.current = node
        self.forward=True
    def next(self):
        if self.forward:
            #...
class LinkedList(...):
    def __iter__(self):
        return Iterator(self.head)
    # rückwärts & extras brauchen zus. Methoden 
    #oder gehören in abgeleitete Klassen

hth, Jörg
Wir haben schon 10% vom 21. Jahrhundert hinter uns!
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

@ b.esser-wisser

ja so hab ich es ja vor. nur wollte ich die syntax beim aufruf von

Code: Alles auswählen

for n in mylinkedlist:
 pass # do anything
schön einfach halten. einfach rückwärts iterieren geht hier nicht.
dachte da an

Code: Alles auswählen

for n in mylinkedlist[::-1]:
 pass # do anything
muss doch irgendwie möglich sein an die __iter__ methode ein arg zu übergeben, das dann den richtigen iterator ansteuert.

momentan habe ich es mit generatoren gelöst.

Code: Alles auswählen

def list_for_each(list, item = None):

    ''' list_for_each() - iterate over a list '''

    head = list.head
    item = (item.next if item != None else list.head.next)

    while item != head:
        yield item
        item = item.next 

def list_for_each_prev(list, item = None):

    ''' '''

    head = list.head
    item = (item.prev if item != None else list.head.prev)

    while item != head:
        yield item
        item = item.prev
nur ists hier immer blöd aus dem modulnamensraum heraus die methode aufzurufen. sprich mit

Code: Alles auswählen

 from mylinkedlist import iterators
und dann mit

Code: Alles auswählen

 
for n in iterators.list_for_each(meineZuDurchlaufendeListe):
 pass # do anything
anzustoßen.

Ich dachte das kann man irgendwie einfach mit:

Code: Alles auswählen


for n in meineLinkedListe: pass

machen[/code]
be or not to be
Benutzeravatar
b.esser-wisser
User
Beiträge: 272
Registriert: Freitag 20. Februar 2009, 14:21
Wohnort: Bundeshauptstadt B.

Code: Alles auswählen

for n in iterators.list_for_each(meineZuDurchlaufendeListe):
 pass # do anything
Nein, ich dachte da an eine Methode:

Code: Alles auswählen

for n in meine_zu_durchlaufende_liste.backwards():
    pass
:wink:
hth, Jörg
ps.:
llist[::1] geht über __getitem__, __setitem__ und sog. slice-Objekte, ist aber wohl erstmal nichtso wichtig
Wir haben schon 10% vom 21. Jahrhundert hinter uns!
BlackJack

@maxwell: Gehört Deine `__iter__`-Methode zur `next()`-Methode? Dann ist sie ungewöhnlich, weil ein `iter()` angewendet auf einen Iterator eben jenen Iterator *unverändert* zurückgeben sollte, und nicht wieder von vorne anfangen sollte.

Ansonsten würde ich das wie folgt lösen:

Code: Alles auswählen

class LinkedList(object):
    def __iter__(self):
        item = self.head
        while item is not None:
            yield item
            item = item.next
    
    def __reversed__(self):
        item = self.last
        while item is not None:
            yield item
            item = item.previous
Das `__reversed__()` von der `reversed()`-Funktion aufgerufen wird ist zwar AFAIK nicht dokumentiert, scheint aber bei CPython immer der Fall zu sein.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Warum willst du überhaupt einen Iterator für eine Liste implementieren? Die ist doch schon iterierbar. Und für Rückwärts-Iterieren gibts den `reverse`-Iterator.

Gruß
BlackJack

@lunar: Jaaa, in der Doku für 2.6. Wer benutzt denn schon so etwas Neumodisches!? :-) Bei 2.5 ist das noch nicht dokumentiert, funktioniert aber trotzdem. Zumindest bei CPython.
lunar

@BlackJack: Man muss eben mit der Zeit gehen ;)
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

@all

just in time nach einer argen Google-Orgie bin ich auf
__reversed__() gestoßen. wollte gerade noch mal schauen ob noch jemand geantwortet hat und zack... klasse sache.

aber leider habe ich immer noch bauchschmerzen. das gefällt mir alles noch nicht so ganz.

__reversed__() nimmt keine args an.

ich möchte jedoch gerne auch einen startknoten angeben können.

bsp.

Code: Alles auswählen

 for i in myList(startKnoten): pass 
ich denke das geht dann nur über slices...

vielen dank euch alle!
vg chris
be or not to be
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

maxwell hat geschrieben:ch möchte jedoch gerne auch einen startknoten angeben können.

bsp.

Code: Alles auswählen

 for i in myList(startKnoten): pass 
Wenn myList ein Exemplar einer eigenen Klasse ist, dann gib der Klasse doch eine Methode die einen solchen Generator erzeugt:

Code: Alles auswählen

for i in myList.from_here(start_knoten)
Sonst schreibst du eine Generatorfunktion, welche myList und den Startwert als Parameter bekommt:

Code: Alles auswählen

for i in from_here(myList, start_knoten)
Das Leben ist wie ein Tennisball.
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

@EyDu

so hab ichs momentan auch gemacht.

Code: Alles auswählen

    def __iter__(self, i):
        
        ''' __iter__() - called if you iterate over list items with 
        "for" syntax '''
    
        return self.forward()

    def __reversed__(self):
    
        ''' '''

        return self.backward()

    def forward(self, item = None):

        ''' forward() - iterate over a list 
        @item: an entry from which we will start '''

        head = self.head
        item = (item.next if item != None else head.next)

        while item != head:
            yield item
            item = item.next 

    def backward(self, item = None):

        ''' backward() - iterate over a list backwards '''

        head = self.head
        item = (item.prev if item != None else head.prev)

        while item != head:
            yield item
            item = item.prev
ich hätte es nur gerne beispielsweise über diesen
ausdruck geschafft:

Code: Alles auswählen

 for i in myList[5::]: pass  
be or not to be
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Dazu könntest du __getitem__ überschreiben. Das macht aber nur wirklich Sinn, wenn es echte Sequenzen sind, auf deren Elemente per Index zugegriffen werden kann. Alles andere wäre etwas verwirrend.
Das Leben ist wie ein Tennisball.
Antworten