iteratoren rumreichen und warum kein iter.hasnext?

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
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

Freitag 24. April 2009, 11:57

tag,

habe zwei fragen zu iteratoren.

ich hangele mich durch ein xml (per etree.ElementDepthFirstIterator).
dabei durchläuft eine schelife den iterator und übergibt den kontextknoten an eine funktion. findet die was von interesse werden alle nachkommen des elements nicht mehr benötigt, sie lässt den iter also bis zum nächsten sibling durchlaufen.

schematisch in etwa so:

Code: Alles auswählen

def skipsome(x, it):
	print x
	if x == 10:
		while(x<15):
			x=it.next()

it = iter(range(0,20))

while True: 
	try: 
		skipsome(it.next(), it)
	except StopIteration: 
		break 
mir kommt das irgendwie seltsam vor, wie würdet ihr das machen?

2. frage:
warum gibt es kein hasnext oä?
"except StopIteration" finde ich nicht grade hübsch...
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Freitag 24. April 2009, 13:04

Dill hat geschrieben:2. frage:
warum gibt es kein hasnext oä?
"except StopIteration" finde ich nicht grade hübsch...
Weil es nicht nötig ist. Man ruft ``next()`` ja normalerweise nicht selbst auf, sondern nutzt eine for-Schleife und die terminiert von selbst wenn es keine weiteren Daten gibt.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Samstag 25. April 2009, 08:48

Dies könnte funktionieren (sieht allerdings recht kompliziert aus, daher bin ich nicht sicher):

Code: Alles auswählen

class JavaStyleIterator(object):
    def __init__(self, itr):
        self.itr = itr
        self.nxt = self
    
    def __iter__(self):
        return self
    
    def next(self):
        if self.hasNext():
            nxt = self.nxt
            self.nxt = self
            return nxt
        raise StopIteration
    
    def hasNext(self):
        if self.nxt is self:
            try:
                self.nxt = self.itr.next()
                return True
            except StopIteration:
                self.nxt = self
                return False
        return True
Stefan
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

Samstag 25. April 2009, 09:06

Weil es nicht nötig ist. Man ruft ``next()`` ja normalerweise nicht selbst auf, sondern nutzt eine for-Schleife und die terminiert von selbst wenn es keine weiteren Daten gibt.
für was brauche ich dann überhaupt einen iterator, dann kann ich doch direkt über eine liste laufen.


@sma
ich bezweifele ja nicht, dass man das implementieren kann, ich frage mich nur, warum es nicht built-in ist.

dein vorschlag ist mir auch etwas schwer verdaulich, da bleib ich lieber beim "manuellen" ansatz. (mir fällt jetzt aber auch nicht ein, wei man das anders lösen könnte)
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Samstag 25. April 2009, 09:10

Dill hat geschrieben:für was brauche ich dann überhaupt einen iterator, dann kann ich doch direkt über eine liste laufen.
Ein Iterator ist ein Mittel zum Zweck, kein Selbstzweck. Iteratoren sind ja nur ein Protokoll. Wenn du ein Objekt haben willst, durch das du iterieren willst, direkt, ohne zwischengeschaltete Liste implementierst du dieses Protokoll und ``for`` weiß dann sofort was man tun muss.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
BlackJack

Samstag 25. April 2009, 11:05

@Dill: Statt der ``while``-Schleife mit der Ausnahmebehandlung kannst Du auch eine viel einfachere ``for``-Schleife verwenden, weil da diese Ausnahmebehandlung ja schon drin steckt:

Code: Alles auswählen

def skipsome(x, it):
    print x
    if x == 10:
        while x < 15:
            x = it.next()

def main():
    it = iter(range(0, 20))
    
    for x in it:
        skipsome(x, it)
Zur `hasNext()`-Frage: Das würde Iteratoren komplizierter machen. Insbesondere bedeutet das ja auch, dass `hasNext()` nicht nur eine einfache Abfrage eines Zustands ist, sondern unter Umständen weitreichende Seiteneffekte haben kann, denn bei vielen Iteratoren muss man intern tatsächlich das nächste Element holen/erstellen um sagen zu können ob es eines gibt.
BlackJack

Samstag 25. April 2009, 11:08

@sma: Interessante Verwendung von `self` als Sentinel. :-)

Allerdings könnte ja der exotische Fall auftreten, dass das "iterable" diesen Iterator selbst enthält, dann funktioniert's nicht mehr.
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Samstag 25. April 2009, 12:41

ich nehm in der Regel

Code: Alles auswählen

null = object()
oder so :o
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

Samstag 25. April 2009, 14:50

BlackJack hat geschrieben:@Dill: Statt der ``while``-Schleife mit der Ausnahmebehandlung kannst Du auch eine viel einfachere ``for``-Schleife verwenden, weil da diese Ausnahmebehandlung ja schon drin steckt:

Code: Alles auswählen

def skipsome(x, it):
    print x
    if x == 10:
        while x < 15:
            x = it.next()

def main():
    it = iter(range(0, 20))
    
    for x in it:
        skipsome(x, it)
aaahh, sehr schön.
jetzt wird mir auch der unterschied klar. ich bin davon ausgegangen, dass das for eine liste aus dem iterator macht. ist natürlich quatsch.
so ist das natürlich viel toller als ne schleife über ne liste.

erstmal geht das damit schonmal garnicht (wahrscheinlich kriegt ihr das irgendiwe hingebogen, aber der naive ansatz schlägt fehl:

Code: Alles auswählen

def skipsome(x, lst): 
    print x 
    if x == 10:
        s = x + 1
        for x in lst[s:]:
            if x == 15: break
            print "skip ", x

def main(): 
    lst = range(0, 20)
    
    for x in lst: 
        skipsome(x, lst)

...
8
9
10
skip  11
skip  12
skip  13
skip  14
11
12
...


was ich aber richtig schön finde ist, dass konsequenterweise auch das geht:

Code: Alles auswählen

def skipsome(x, it): 
    print x 
    if x == 10: 
        # (!)
        for x in it: 
            if x == 15: break 

def main(): 
    it = iter(range(0, 20)) 
    
    for x in it: 
        skipsome(x, it)
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Sonntag 26. April 2009, 09:51

Wenn ich dich richtig verstehe, willst du einfach den Zustand des Iterators ändern, also bestimmte Elemente mittels skipsome ausschließen lassen, um dann außerhalb ohne diese Elemente weiterzumachen? Da ist dein Ansatz nicht der Eleganteste.

Zunächst einmal kannst du doch deine skipsome Funtion zu einer Wahrheitsfunktion umbauen, die einfach True / False zurückgibt, und ihr einen passenden Namen geben. skip_if_whatever oder sö. Dann kannst du dir einfach eine neue Liste aus der alten Generieren lassen:

Code: Alles auswählen

my_new_list = [obj for obj in it if not skip_if_whatever(obj)] # Mittels List Comprehension

import itertools
my_new_list = list(itertools.ifilterfalse(skip_if_whatever, it)) # Siehe auch builtin filter, itertools.ifilter
Wenn du deinen ursprünglichen Ansatz benutzen willst, musst du einen Iterator statt einer Liste übergeben, wie in deinem zweiten Beispiel.

Ein Iterator besitzt immer eine __iter__ Methode und eine next (bzw __next__ mit Python 3) Methode. Eine for Schleife ruft immer iter() auf (welches wiederrum __iter__ auf dem Objekt aufruft). Eine Liste generiert jedesmal einen neuen "Listeniterator". Generierst du den aber vorher selbst und übergibst ihn, wird bei erneutem Aufruf von iter() immer der eigentliche Iterator selbst zurückgeben. Also so:

Code: Alles auswählen

class MyIterator(object):
    def __iter__(self):
        return self
    def next(self):
        ...
Das ist das Standarditerator"protokoll" und jeder Iterator verhält sich so. Dinge, die Iteratoren in __iter__ generieren, generieren immer solche Iteratoren. Wenn man selbst Iteratoren baut, sollte man sie auch so funktionieren lassen.

Folge: Bei deinem zweiten Beispiel wird der Zustand des Iterators verändert nach Aufruf von skipsome. Die Forschleife macht dann mit dem verändertem Iterator weiter:

Code: Alles auswählen

In [1]: def skipsome(x, it):
   ...:         print x
   ...:     if x == 10:
   ...:             while x < 15:
   ...:                 x = it.next()
   ...: 

In [2]: def main():
   ...:         it = iter(range(0, 20))
   ...:    
   ...:     for x in it:
   ...:             skipsome(x, it)
   ...: 

In [3]: main()
0
1
2
3
4
5
6
7
8
9
10
16
17
18
19
Aber du solltest wirklich sehen, das du den ersten Ansatz benutzt.
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

Sonntag 26. April 2009, 11:21

str1442 hat geschrieben:
Zunächst einmal kannst du doch deine skipsome Funtion zu einer Wahrheitsfunktion umbauen, die einfach True / False zurückgibt, und ihr einen passenden Namen geben. skip_if_whatever oder sö. Dann kannst du dir einfach eine neue Liste aus der alten Generieren lassen:
das funktioniert in meinem fall nicht, da ich komplette node-sets wegwerfen will.
Folge: Bei deinem zweiten Beispiel wird der Zustand des Iterators verändert nach Aufruf von skipsome. Die Forschleife macht dann mit dem verändertem Iterator weiter:
ja, das is aber auch genau das was ich erreichen will.
Antworten