itertools.cycle - jetzt auch rückwärts

Code-Stücke können hier veröffentlicht werden.
Antworten
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Ich habe mir eine Klasse geschrieben, um iterables nicht nur vorwärts durchlaufen zu können (mit der itertools.cycle Funktion), sondern auch rückwärts. Hier der Code: http://paste.pocoo.org/show/116157/. Und hier ein Beispiel, wie man diese Klasse einsetzen kann:

Code: Alles auswählen

In [594]: numbers = Cycler(('one', 'two', 'three', 'four', 'five'))

In [595]: numbers.next()
Out[595]: 'one'

In [596]: numbers.next()
Out[596]: 'two'

In [597]: numbers.prev()
Out[597]: 'one'

In [598]: numbers.prev()
Out[598]: 'five'

In [599]: numbers.prev()
Out[599]: 'four'

In [600]: numbers.prev()
Out[600]: 'three'
Eine wichtige Bedingung ist, dass es keine Duplikate im iterable geben darf. Sonst passiert z.B. folgendes, weil sich die Klasse immer den ersten gefunden Wert als current_value speichert:

Code: Alles auswählen

In [608]: letters = Cycler(['a', 'b', 'a', 'd'])

In [609]: letters.next()
Out[609]: 'a'

In [610]: letters.next()
Out[610]: 'b'

In [611]: letters.next()
Out[611]: 'a'

In [612]: letters.prev()
Out[612]: 'd'
Jemand eine Idee, wie man das vermeiden kann?
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Ich würde gar nicht auf cycle() zurückgreifen, sondern den aktuellen Index selbst mitzählen.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Gute Idee. Jetzt sieht es so aus (lästige Wiederholungen sind natürlich Absicht ;)): http://paste.pocoo.org/show/116172/ Damit ist mein Problem gelöst. Ein wenig mehr Ästhetik kann der Code natürlich noch vertragen.
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Wobei die Bezeichnung "iterable" recht irreführend ist, da Iteratoren kein Indexing können müssen und die allerwenigsten das unterstützen.

Code: Alles auswählen

>>> iterable = iter([])
>>> iterable[0]
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: 'listiterator' object is unsubscriptable
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Und "Cycler" ist auch nicht unbedingt der richige Name:

Code: Alles auswählen

>>> l = [1,2]
>>> c = Cycler(l)
>>> c.next()
1
>>> c.next()
2
>>> c.next()
1
>>> c.next()
2
>>> c.next()
Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    c.next()
  File "C:/Programme/Python30/test.py", line 18, in next
    return self.iterable[self.current_index - len(self.iterable)]
IndexError: list index out of range
>>> 
Und etwas gekürzt:

Code: Alles auswählen

class Cycler(object):
    """cycle through a list — both forward and backward!"""
    def __init__(self, iterable):
        self.iterable = iterable
        self.current_index = 0

    def step(self, direction):
        result = self.iterable[self.current_index]
        self.current_index = (self.current_index + direction) % len(self.iterable)
        return result
    
    def next(self):
        return self.step(1)

    def prev(self):
        return self.step(-1)
Das Leben ist wie ein Tennisball.
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Was für einen Namen könnte man denn statt iterable nehmen? indexable_object hört sich auch nicht gut an, finde ich. EyDu: Danke für den Hinweis und deinen Code. Das sieht schon viel eleganter aus; diese Klasse werde ich in meinem Skript benutzen.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Indexierbar reicht als Eigenschaft auch noch nicht aus: es muss irgend eine Ordnung zwischen den Indizes bestehen, hier von 0 bis n-1 und die Länge muss ebenfalls bekannt sein. Schreib die benötigten Eigenschaften doch so in die Dokumentation und erwähne als Beispiel Listen und Tupel. Dann weiß schon jeder, was gemeint ist.
Das Leben ist wie ein Tennisball.
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

ok, das schreibe ich dann in den docstring von __init__. Aber welchen Namen sollte ich für den Parameter wählen?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Sequence sollte passen. Da hätte man auch früher drauf kommenkönnen ^^
Das Leben ist wie ein Tennisball.
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Stimmt, damit fällt dann auch der docstring in __init__ weg. Super!
Antworten