Methode in list-abgeleiteter Klasse

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
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

Wie definiere ich folgende Methode "circulate" meiner Klasse "roundlist" richtig?

Code: Alles auswählen

class roundlist(list):
   def __getitem__(self,i):  return super().__getitem__(i%len(self))
   def __setitem__(self,i,v):  super().__setitem__(i%len(self),v)
   def __delitem__(self,i):  super().__delitem__(i%len(self))

   def insert(self,i,x):  super().insert(i%len(self),x)
   def pop(self,i):  super().pop(i%len(self))

   #HIER ist etwas FALSCH, aber was bloß?
   def circulate(self,k):  return self[k:]+self[:k]
   #
   #def __getitem__(self,i):  return super().__getitem__(i%len(self))
   #TypeError: unsupported operand type(s) for %: 'slice' and 'int'


kreis = roundlist([0,1,2,3,4])
print(kreis)
kreis.circulate(2)  #erwartet wird [2,3,4,0,1]
print(kreis)
Zuletzt geändert von Goswin am Donnerstag 13. Dezember 2012, 11:22, insgesamt 1-mal geändert.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Siehe collections.deque.rotate
BlackJack

@Goswin: Du machst mit dem Rückgabewert Deiner Methode nichts. Oder wolltest Du dass sich im Beispiel `kreis` *verändert*? Das macht Dein Code in `circulate()` nicht.

Sowohl `roundlist` als auch `circulate()` sind sehr unübliche Namen für das was Du da machst. Die Struktur heisst gewöhnlich `ring_buffer` und die Operation `rotate()`.

Ich würde auch nicht von `list` erben wenn nicht alle Operationen davon verwendet werden können und ein irgendwie sinnvoll geartetes Ergebnis geliefert wird.
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

@BlackJack: Du hast insofern recht, als da noch ein zweiter Fehler war, der von mir unbemerkt blieb, weil das Programm vorher abstürzte. Aber wenn ich diesen zweiten Fehler berichtige, bleibt der ursprüngliche Fehler bestehen. Meine Objekte werden *definitiv nicht* als Buffer benutzt, und alle restlichen Methoden außer 'sort' und 'sorted' sind sinnvoll; auch enthält die Klasse noch weitere Methoden, die ich hier der Einfachheit halber fortlasse. Hier die berichtigte Version mit 'rotate':

Code: Alles auswählen

class ringlist(list):
   def __getitem__(self,i):  return super().__getitem__(i%len(self))
   def __setitem__(self,i,v):  super().__setitem__(i%len(self),v)
   def __delitem__(self,i):  super().__delitem__(i%len(self))

   def insert(self,i,x):  super().insert(i%len(self),x)
   def pop(self,i):  super().pop(i%len(self))

   #> hier weitere Methoden <#

   #HIER ist etwas FALSCH, aber was bloß?
   #def rotate(self,k):  self = self[k:]+self[:k]  #unrevidiert
   def rotate(self,k):  self[:] = self[k:]+self[:k]  #auch falsch
   #
   #def __getitem__(self,i):  return super().__getitem__(i%len(self))
   #TypeError: unsupported operand type(s) for %: 'slice' and 'int'


kreis = ringlist([0,1,2,3,4])
print(kreis)
kreis.rotate(2)  #erwartet wird [2,3,4,0,1]
print(kreis)
Nebenbei: Wenn ich von 'deque' erbe, dann funktioniert zwar 'rotate', aber dafür funktionieren 'insert' und 'pop' nicht mehr.
Zuletzt geändert von Goswin am Donnerstag 13. Dezember 2012, 11:52, insgesamt 1-mal geändert.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Du musst dem Objekt, auf das self referenziert, auch den Wert zuweisen. self ist in diesem Kontex nur ein lokaler Name.

Code: Alles auswählen

>>> spam = [1,2,3,4,5]
>>> self = spam
>>> self
[1, 2, 3, 4, 5]
>>> spam
[1, 2, 3, 4, 5]
>>> self = [6,7,8,9,0]
>>> self
[6, 7, 8, 9, 0]
>>> spam
[1, 2, 3, 4, 5]
>>> 
>>> self = spam
>>> self
[1, 2, 3, 4, 5]
>>> spam
[1, 2, 3, 4, 5]
>>> self[:] = [6,7,8,9,0]
>>> self
[6, 7, 8, 9, 0]
>>> spam
[6, 7, 8, 9, 0]
Ein guter Zeitpunkt für dich, um sich noch einmal den Unterschied zwischen Variablen und Referenzen zu verdeutlichen.
Das Leben ist wie ein Tennisball.
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

@EyDu: Auch du hast recht, die Berichtigung war auch falsch, aber wie ich schon BlackJack sagte: das ist nicht der Absturzfehler!
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Meinst du den Fehler hier?

Code: Alles auswählen

TypeError: unsupported operand type(s) for %: 'slice' and 'int'
Da erhält __getitem__ ein slice-Objekt und keinen Integer als Index. Da wäre natürlich der gesamte Traceback sinnvoll.
Das Leben ist wie ein Tennisball.
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

@EyDu: Den gesamten Traceback kannst du gerne haben, wenn dir das weiterhilft:

Code: Alles auswählen

Traceback (most recent call last):
  File "./type_ringlist.py", line 134, in <module>
    kreis.rotate(2)
  File "./type_ringlist.py", line 74, in rotate
    self[:] = self[k:]+self[:k]
  File "./type_ringlist.py", line 57, in __getitem__
    def __getitem__(self,i):  return super().__getitem__(i%len(self))
TypeError: unsupported operand type(s) for %: 'slice' and 'int'
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Ok, ich war etwas verwirrt, dass Python 2.x bei mir keinen Fehler wirft. Bei 3.x allerdings schon. Der Fehler war ja auch meine Vermutung:

Code: Alles auswählen

>>> class Eggs(list):
...     def __getitem__(self, val):
...         print(val)
... 
>>> e = Eggs([1,2,3,4,5])
>>> e[1:]
slice(1, None, None)
>>> slice(1, None, None) % 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for %: 'slice' and 'int'
Du must hier also zwischen Slices und Indizes unterscheiden.
Das Leben ist wie ein Tennisball.
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

So hier ist jetzt meine Lösung. Ob sie die beste, eleganteste, oder pythonischte ist, lasse ich dahingestellt:

Code: Alles auswählen

class ringlist(list):

   def __getitem__(self,i):
      try: return super().__getitem__(i%len(self))
      except TypeError: return super().__getitem__(i)

   def __setitem__(self,i,v):
      #try: super().__setitem__(i%len(self),v); return  #unrevidiert
      try: super().__setitem__(i%len(self),v)
      except TypeError: super().__setitem__(i,v)

   def __delitem__(self,i):
      #try: super().__delitem__(i%len(self)); return  #unrevidiert
      try: super().__delitem__(i%len(self))
      except TypeError: super().__delitem__(i)
     
   def rotate(self,k):
      kmod = k%len(self)
      self[:] = self[kmod:]+self[:kmod]
Vielen Dank für eure Einfälle!
Zuletzt geändert von Goswin am Donnerstag 13. Dezember 2012, 16:02, insgesamt 1-mal geändert.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Goswin hat geschrieben:So hier ist jetzt meine Lösung. Ob sie die beste, eleganteste, oder pythonischte ist, lasse ich dahingestellt
Ich mag ja diese try-except-Einzeiler gar nicht.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Und die leeren returns sollten auch noch gestrichen werden. Und vielleicht ein vernünftiger Name für die Klasse.
Das Leben ist wie ein Tennisball.
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

EyDu hat geschrieben:Und die leeren returns sollten auch noch gestrichen werden. Und vielleicht ein vernünftiger Name für die Klasse.
Jetzt sind sie weg, die überflüssigen returns. Die Versionszahl _01 im Klassennamen war ein Typo.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Man kann natürlich auch noch ein wenig zusammenfassen.

Code: Alles auswählen

def __getitem__(self, index):
    try:
        index = index % len(self))
    except TypeError:
        pass

    return super().__getitem__(index)
Edit: index.
Zuletzt geändert von EyDu am Donnerstag 13. Dezember 2012, 17:03, insgesamt 1-mal geändert.
Das Leben ist wie ein Tennisball.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Mir ist nicht klar, warum Du einen TypeError abfängst, dann aber an super delegierst. Dort wird diese Exception dann erneut geworfen.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

kbr hat geschrieben:Mir ist nicht klar, warum Du einen TypeError abfängst, dann aber an super delegierst. Dort wird diese Exception dann erneut geworfen.
Weil der TypeError vom Modulo-Operator kommt.
Das Leben ist wie ein Tennisball.
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

kbr hat geschrieben:Mir ist nicht klar, warum Du einen TypeError abfängst, dann aber an super delegierst. Dort wird diese Exception dann erneut geworfen.
:) Wieso wird sie neu geworfen? Der Argumentwert ist doch ein anderer!
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Oops, jetzt bin ich auch über die Slices gestolpert ... klar.
Antworten