Seite 1 von 1

Generatoren & Iteratoren

Verfasst: Dienstag 18. August 2009, 09:02
von maxwell
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

Verfasst: Dienstag 18. August 2009, 09:10
von Defnull
Zu wenig Input.
Woher kommen die Links? SQL?
Wie hast du __iter__ implementiert?

Verfasst: Dienstag 18. August 2009, 09:21
von maxwell
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

Verfasst: Dienstag 18. August 2009, 09:38
von b.esser-wisser
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

Verfasst: Dienstag 18. August 2009, 09:48
von maxwell
@ 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]

Verfasst: Dienstag 18. August 2009, 09:58
von b.esser-wisser

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

Verfasst: Dienstag 18. August 2009, 12:58
von 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.

Verfasst: Dienstag 18. August 2009, 13:03
von Dauerbaustelle
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ß

Verfasst: Dienstag 18. August 2009, 13:23
von lunar

Verfasst: Dienstag 18. August 2009, 13:40
von 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.

Verfasst: Dienstag 18. August 2009, 13:47
von lunar
@BlackJack: Man muss eben mit der Zeit gehen ;)

Verfasst: Dienstag 18. August 2009, 13:54
von maxwell
@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

Verfasst: Dienstag 18. August 2009, 14:10
von EyDu
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)

Verfasst: Dienstag 18. August 2009, 14:17
von maxwell
@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  

Verfasst: Dienstag 18. August 2009, 14:45
von EyDu
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.