Objekt zu einem iterablen objekt erweitern

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
...
User
Beiträge: 116
Registriert: Mittwoch 23. Dezember 2009, 20:22

Hallo,


Die Aufgabe:
Eine spezielle, selbst definierte Liste ist gegeben.
Jetzt sollen wir damit einige kleinigkeiten machen, bei denen wir jeweils über die liste Iterieren müssen...

Ich dachte mir: Ich muss doch nicht immer den ganzen Iterationskram neu schreiben,
sondern es müsste reichen, wenn ich das Objekt selbst iterierbar mache, so das z.B. for-schleifen funktionieren.

Das Objekt hat 2 attribute
data, der aktuelle wert
next, entweder None (dann wars das letzte glied) oder ein gleichartiges Listenobjekt mit den Restlichen werten.

Ganz grob zusammengebastelt hab ich mir nach kurzer Lektüre der Dokumentation das hier, was auch Funktioniert, aber...

Code: Alles auswählen

class Liste(object):
    def __init__(self...)
        ....

    def __iter__(self):
        return self.__iterobj(self)

    class __iterobj:
        def __init__(self, data):
            self.data = data

        def __next__(self):
            ret = self.data.data
            if self.data.next is None:
                raise StopIteration()
            else:
                self.data = self.data.next
                return ret

        def __iter__(self):
            return self
Was ich jetzt wissen möchte: Wie macht man sowas RICHTIG.
Hab da was von Generator-Klassen und yield gelesen, aber mir fehlt für sowas ein Beispiel, das ich nachvollziehen kann,
wie das dann genau Implementiert wird.


lg,
...
Zuletzt geändert von ... am Mittwoch 2. Februar 2011, 18:11, insgesamt 1-mal geändert.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Code: Alles auswählen

def __iter__(self):
    data = self.data
    while data.next is not None:
        yield data
        data = data.next
...
User
Beiträge: 116
Registriert: Mittwoch 23. Dezember 2009, 20:22

Ok, Danke.
Abgesehen von einigen kleinen Änderungen, die ich vornehmen musste, damit es auf mein Objekt passt
(data hat kein Attribut next, sondern parallel zu data existiert dieses) funktioniert es:

Code: Alles auswählen

def __iter__(self):
    liste = self
    while liste.next is not None:
        yield liste.data
        liste = liste.next
Nur noch eine Frage dazu: In der Doku stand, es müsse zu einer Ausnahme StopIteration kommen, um das sauber abzuschließen.
Muss es desshalb nicht so aussehen:

Code: Alles auswählen

def __iter__(self):
    liste = self
    while liste.next is not None:
        yield liste.data
        liste = liste.next
    raise StopIteartion
?


lg,
...
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Die StopIteration Ausnahme wird implizit geworfen wenn die Ausführung des Generators "beendet" ist.
...
User
Beiträge: 116
Registriert: Mittwoch 23. Dezember 2009, 20:22

Vielen Dank!
BlackJack

@...: Das Generatorobjekt muss *jedes mal* diese Ausnahme auslösen, wenn man versucht ein weiteres Element zu bekommen, und nicht nur einmal:

Code: Alles auswählen

In [22]: def f():
   ....:     for x in xrange(3):
   ....:         yield x
   ....: 

In [23]: g = f()

In [24]: g.next()
Out[24]: 0

In [25]: g.next()
Out[25]: 1

In [26]: g.next()
Out[26]: 2

In [27]: g.next()
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)

/home/bj/<ipython console> in <module>()

StopIteration: 

In [28]: g.next()
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)

/home/bj/<ipython console> in <module>()

StopIteration:
Das ginge ja nicht, wenn das selber am Ende der Funktion nur einmal auslöst.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

BlackJack hat geschrieben:Das ginge ja nicht, wenn das selber am Ende der Funktion nur einmal auslöst.
Wenn man StopIteration auslöst wird Code danach in keinem Fall mehr ausgeführt.

Code: Alles auswählen

>>> def foo():
...     yield 1
...     raise StopIteration()
...     yield 2
... 
>>> gen = foo()
>>> gen.next()
1
>>> gen.next()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 3, in foo
StopIteration
>>> gen.next()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
StopIteration
Dementsprechend könnte ein Generator auch irgendwas anderes tun wenn StopIteration nicht explizit geworfen wird.
BlackJack

@DasIch: Ich verstehe jetzt nicht so ganz was Du damit sagen willst!? Also was Du sagst verstehe ich schon -- ist ja offensichtlich, aber wo ist der Zusammenhang mit dem Zitat!?
...
User
Beiträge: 116
Registriert: Mittwoch 23. Dezember 2009, 20:22

Ich denke er will damit sagen, das raise StopIteration
jedes mal exakt dies tut, und nicht nur einmal.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

@BlackJack Deine Aussage, dass der Generator bei jedem Aufruf StopIteration werfen muss fand ich etwas unklar, da nicht wirklich deutlich wird dass sich Python darum schon kümmert und man dass nicht selbst machen muss.
BlackJack

@DasIch: Das hattest Du doch vorher schon klargestellt und meine Aussage baute darauf auf. Und mein Codebeispiel zeigt es doch auch noch mal recht deutlich. Meine Aussage ist doch, dass Python das machen *muss* weil man das selber gar nicht machen kann.
Antworten