DefaultList

Code-Stücke können hier veröffentlicht werden.
Antworten
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

... weil ich die Funktionalität gerade brauchte:

Für Python 3.3:

Code: Alles auswählen

class DefaultList(list):
    def __init__(self, default=lambda: None, iterable=None):
        super().__init__(iterable or list())
        self._default = default
        
    def __getitem__(self, index):
        if index >= len(self):
            return self._default()
        else:
            return super().__getitem__(index)
    
    def __setitem__(self, index, object_):
        if index >= len(self): #-- Expand list if necessary.
            self.extend([self._default()] * (index - len(self) + 1))
        super().__setitem__(index, object_)

Code: Alles auswählen

>>> foo = DefaultList(lambda: 42, range(10))
>>> foo
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> foo[5] == 5
True
>>> foo[0] = 123
>>> foo[0] == 123
True
>>> foo[30] == 42
True
>>> foo[15] = 321
>>> foo
[123, 1, 2, 3, 4, 5, 6, 7, 8, 9, 42, 42, 42, 42, 42, 321]
Viel Spaß beim Ausschlachten. :mrgreen:

Grüße ... bwbg
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Code: Alles auswählen

    def __setitem__(self, index, object_):
        if index >= len(self): #-- Expand list if necessary.
            self.extend(self._default() for _ in range(index - len(self) + 1))
        super().__setitem__(index, object_)
Und schon klappts auch, wenn `default` mutable Objekte zurueckgibt. Es sei denn du willst da tatsaechlich identische haben ;)
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Hmm... ich überlege gerade, für was man sowas braucht...?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Wieso muss denn überhaupt eine Funktion aufgerufen bzw übergeben werden für die Rückgabe des Default-Arguments? Welchen Vorteil soll das haben?
BlackJack

@snafu: Das zeigt cofi's Korrektur doch ganz deutlich: Wenn man nicht immer das *selbe* Objekt als Default an jedem Index haben möchte, muss man immer neue *erstellen*. Und das ist nun mal mit einem Aufruf verbunden. Und die Kontrolle über das erstellen von neuen Werten hat man nur wenn man dort ein aufrufbares Objekt übergibt. `collections.defaultdict` macht das deshalb ja auch so.
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

cofi hat geschrieben:[...]
Und schon klappts auch, wenn `default` mutable Objekte zurueckgibt. Es sei denn du willst da tatsaechlich identische haben ;)
Und genau ein solches Verhalten erwartete ich auch bei der Signatur. Da habe ich wohl einen klassischen Bug produziert.

snafu hat geschrieben:Wieso muss denn überhaupt eine Funktion aufgerufen bzw übergeben werden für die Rückgabe des Default-Arguments? Welchen Vorteil soll das haben?
Ich hatte folgenden Aufruf im Kopf (Bsp. verschachtelte Liste):

Code: Alles auswählen

foo = DefaultList([])
Das funktioniert so nicht (bzw. nicht so, wie man erwarteten sollte). Da ich auf [] als Funktionsargument schon sehr früh gestolpert bin, habe ich mich für eine Funktion entschieden. So funktioniert

Code: Alles auswählen

foo = DefaultList(list)
wie erwartet (nach cofi's Fix).


Halb-OT: Würde man für eine solche Klasse schon unittests anlegen?

Grüße ... bwbg
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Ja, denn mit guten Tests hättest du den Bug selber gefunden. Tests würde ich nur dann *nicht* schreiben, wenn eine Funktion sehr trivial ist, z.b. wenn sie nichts anderes tut als eine andere Funktion mit einem konstanten Wert aufruft.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

bwbg hat geschrieben:Für Python 3.3:
Unter einem hinreichend aktuellen Python 2 läuft es natürlich auch. Man muss nur die Aufrufe von super() durch super(DefaultList, self) ersetzen.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

bwbg hat geschrieben:Halb-OT: Würde man für eine solche Klasse schon unittests anlegen?
Ja, davor und als "Vertrag" formuliert. Ob du den Fehler mit _Unit_tests gefunden haettest bezweifel ich aber. Unittests testen ja per Definition nur die eine Klasse, aber der Bug zeigt sich nur im Zusammenspiel mit anderen Klassen.
Insofern gibt deine Session zwar wunderbare Unittests her, aber du braeuchtest schon einen "hoeherstufigen" Test, um den Fehler zu finden. Die lassen sich aber auch mit `unittest` (pytest, nose, ...) schreiben, falls du _das_ meintest.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich hab hier mal ein Beispiel gebastelt, welches ohne "erzwungene" Aufrufbarkeit des Default-Arguments auskommt.

In der Anwendung sieht das so aus:

Code: Alles auswählen

>>> foo = DefaultList(range(10), 42)
>>> foo[30]
42
>>> foo[15] = 321
>>> foo
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 42, 42, 42, 42, 42, 321]
>>> foo = DefaultList(range(10), list)
>>> foo
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> foo[15] = 321
>>> foo
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, [], [], [], [], [], 321]
>>> foo[11].append("foo")
>>> foo
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, [], ['foo'], [], [], [], 321]
Die Klasse erkennt also automatisch, ob es sich um ein aufrufbares Default-Argument handelt und tut dann selbiges. Optional kann man auch mittels Wahrheitswert angeben, ob man Aufrufbarkeit wünscht oder nicht. Kann einem so besser gefallen. Muss aber nicht. ;)
BlackJack

@snafu: Ich finde das ein bisschen zu viel Magie. Und mit dem `defaultdict` gibt es ja schon eine API-Entscheidung die man bei ähnlichen Datentypen IMHO nicht ändern sollte. So wegen der Erwartungshaltung von Programmierern und so.
Antworten