Seite 1 von 1

Objekt zu einem iterablen objekt erweitern

Verfasst: Mittwoch 2. Februar 2011, 17:45
von ...
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,
...

Re: Objekt zu einem iterablen objekt erweitern

Verfasst: Mittwoch 2. Februar 2011, 17:52
von DasIch

Code: Alles auswählen

def __iter__(self):
    data = self.data
    while data.next is not None:
        yield data
        data = data.next

Re: Objekt zu einem iterablen objekt erweitern

Verfasst: Mittwoch 2. Februar 2011, 18:04
von ...
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,
...

Re: Objekt zu einem iterablen objekt erweitern

Verfasst: Mittwoch 2. Februar 2011, 18:25
von DasIch
Die StopIteration Ausnahme wird implizit geworfen wenn die Ausführung des Generators "beendet" ist.

Re: Objekt zu einem iterablen objekt erweitern

Verfasst: Mittwoch 2. Februar 2011, 18:38
von ...
Vielen Dank!

Re: Objekt zu einem iterablen objekt erweitern

Verfasst: Mittwoch 2. Februar 2011, 18:42
von 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.

Re: Objekt zu einem iterablen objekt erweitern

Verfasst: Mittwoch 2. Februar 2011, 18:58
von DasIch
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.

Re: Objekt zu einem iterablen objekt erweitern

Verfasst: Mittwoch 2. Februar 2011, 19:02
von 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!?

Re: Objekt zu einem iterablen objekt erweitern

Verfasst: Mittwoch 2. Februar 2011, 21:29
von ...
Ich denke er will damit sagen, das raise StopIteration
jedes mal exakt dies tut, und nicht nur einmal.

Re: Objekt zu einem iterablen objekt erweitern

Verfasst: Mittwoch 2. Februar 2011, 23:02
von DasIch
@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.

Re: Objekt zu einem iterablen objekt erweitern

Verfasst: Mittwoch 2. Februar 2011, 23:22
von 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.