3 Stimmen

Benutzerdefinierter Wrapper zum Indizieren von Python-Listen ab 1

Ich möchte gerne einen einfachen Wrapper für den Python list-Typ schreiben, der ihn zwingt, bei 1 anstatt bei 0 zu indizieren. Ich habe ein ziemlich komplexes Programm, das auf einigen diskreten Wahrscheinlichkeitsverteilungen von Dauerdaten basiert, mit Eimerlängen ganzer Zahlen, aber ich habe keine Dauern von weniger als 1. Wie auch immer, es würde einige signifikante Teile meines Codes erheblich vereinfachen, nahtlos bei 1 zu beginnen. Zuerst habe ich einen dict verwendet, aber ich fand einige Eigenschaften von ihnen zu umständlich.

I've never written a wrapper for a Python class before, much less a built-in type, but I feel like what I want to do is fairly simple. At the bare minimum, I should be able to do stuff like this:

>>> p = one_list([1,2,3,4,5])
>>> for i in range(1,6):
    print i, p[i]

1 1
2 2
3 3
4 4
5 5
>>> len(p)
5

Allerdings wäre es gut, wenn ich auch einige der anderen relevanten eingebauten Methoden der list-Klasse überschreiben könnte, wie z.B. index.

>>> len(p)
5
>>> p.index(p[-1])
5

Bitte teilen Sie Ihre Tipps, wie ich so etwas machen könnte. Ich überlege, ob ich es einfach mit einer benutzerdefinierten Klasse machen soll, aber das scheint mir übertrieben zu sein. Ich freue mich auch über Empfehlungen für nützliche Methoden zum Überschreiben.

Bearbeitung: Nachwort

Ich möchte nur darauf hinweisen, dass es nicht die Mühe wert war, das zu tun, und der Grund, warum ich die untenstehende Antwort akzeptiert habe, ist nicht, weil ich versucht habe, es auf die von ihm beschriebene Weise zu implementieren, sondern weil er mir geholfen hat zu erkennen, dass Listen an sich gut genug sind.

3voto

kindall Punkte 167554

Hier ist eine vollständige Implementierung (ich denke) einer 1-basierten Liste, die das Slicing korrekt behandelt (einschließlich erweiterter Slices), Indizierung, Popping usw. Es ist etwas kniffliger, dies richtig hinzubekommen, als man denken könnte, insbesondere das Slicing und die negativen Indizes. Tatsächlich bin ich mir immer noch nicht zu 100% sicher, ob es genau so funktioniert, wie es sollte, also Vorsicht, Programmierer.

class list1(list):
    """Einbasierte Version einer Liste."""

    def _zerobased(self, i):
        if type(i) is slice:
            return slice(self._zerobased(i.start),
                         self._zerobased(i.stop), i.step)
        else:
            if i is None or i < 0:
                return i
            elif not i:
                raise IndexError("Element 0 existiert nicht in 1-basierter Liste")
            return i - 1

    def __getitem__(self, i):
        return list.__getitem__(self, self._zerobased(i))

    def __setitem__(self, i, value):
        list.__setitem__(self, self._zerobased(i), value)

    def __delitem__(self, i):
        list.__delitem__(self, self._zerobased(i))

    def __getslice__(self, i, j):
        print i,j
        return list.__getslice__(self, self._zerobased(i or 1),
                                 self._zerobased(j))

    def __setslice__(self, i, j, value):
        list.__setslice__(self, self._zerobased(i or 1),
                          self._zerobased(j), value)

    def index(self, value, start=1, stop=-1):
        return list.index(self, value, self._zerobased(start),
                          self._zerobased(stop)) + 1

    def pop(self, i):
        return list.pop(self, self._zerobased(i))

ExtraItemList von senderle wird jedoch eine bessere Leistung haben, da es die Indizes nicht ständig anpassen muss und auch keine zusätzliche Schicht von (nicht-C!) Methodenaufrufen zwischen Ihnen und den Daten hat. Ich wünschte, ich hätte diesen Ansatz bedacht; vielleicht könnte ich ihn profitabel mit meinem kombinieren...

1voto

senderle Punkte 135243

Okay, nun, du scheinst sehr entschlossen zu sein. Wie oben erwähnt, müssen Sie __getitem__, __setitem__ und __delitem__ überschreiben. Tatsächlich müssen Sie auch __getslice__, __setslice__, __delslice__ überschreiben (sonst beginnt das Slicen wie üblich bei 0). Dann __add__, __mul__, __iadd__, __imul__, um auch WonkyLists zurückzugeben (sonst funktioniert die Konkatenation nicht so, wie Sie möchten). Sie müssen index überschreiben, da es einen falschen Wert zurückgibt (Ich habe es getestet). Auch insert, remove und pop. Hier ist etwas, um Ihnen den Einstieg zu erleichtern:

class WonkyList(list):
    def __getitem__(self, index):
        return super(WonkyList, self).__getitem__(index - 1)
    def __setitem__(self, index, val):
        super(WonkyList, self).__setitem__(index - 1, val)
    def __delitem__(self, index):
        super(WonkyList, self).__delitem__(index - 1)

Getestet:

>>> w = WonkyList(range(10))
>>> w[1]
0
>>> del w[5]
>>> w
[0, 1, 2, 3, 5, 6, 7, 8, 9]
>>> w[1] = 10
>>> w
[10, 1, 2, 3, 5, 6, 7, 8, 9]

Hier sind einige Möglichkeiten, wie es scheitern wird, bis Sie alle erforderlichen Methoden überschreiben:

>>> w
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> w[5]
4
>>> w.pop(5)
5
>>> w.insert(5, 5)
>>> w
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> del w[2:4]
>>> w
[0, 1, 4, 5, 6, 7, 8, 9]
>>> 
>>> w.index(1)
1

Oder Sie könnten etwas anderes ausprobieren. Haben Sie zum Beispiel schon einmal darüber nachgedacht...

def ExtraItemList(list):
    def __init__(self, seq):
        self[0] = 0
        self[1:] = seq
    def n_items(self):
        return len(self) - 1

Das scheint die von Ihnen aufgelisteten Probleme zu lösen, und es verhält sich immer noch weitgehend wie eine Liste, auch wenn der Initialisierer etwas seltsam ist. Obwohl um ehrlich zu sein, wenn ich mir die von Ihnen gegebenen Beispiele anschaue, fange ich an zu denken, dass auch dies eine hässliche Lösung ist -- keine der Funktionen, die Sie gegeben haben, zeigt den Vorteil, bei 1 zu indizieren, sie zeigen nur die schlechten Auswirkungen des Hinzufügens eines zusätzlichen Elements am Anfang der Liste. Basierend auf diesen Beispielen sollten Sie einfach eine reguläre Liste verwenden.

Vielleicht wäre der beste Ansatz, benutzerdefinierte get und set Methoden zu schreiben, die den gewünschten Offset verwenden. Auf diese Weise würden sich die Listen normal verhalten, aber Sie hätten bei Bedarf eine alternative Schnittstelle.

1voto

Gareth Rees Punkte 62623

Hier ist eine grundlegende Implementierung, um Ihnen den Einstieg zu erleichtern. Ich dachte, es wäre schön, es zu verallgemeinern, damit Sie Listen mit beliebigen ganzen Zahlen als Startindizes haben können. Dies führte mich zur Entdeckung von __slots__, also danke, dass Sie diese Frage gestellt haben!

class OffsetList(list):
    __slots__ = 'offset'
    def __init__(self, init=[], offset=-1):
        super(OffsetList, self).__init__(init)
        self.offset = offset
    def __getitem__(self, key):
        return super(OffsetList, self).__getitem__(key + self.offset)
    def __setitem__(self, key, value):
        return super(OffsetList, self).__setitem__(key + self.offset, value)
    def __delitem__(self, key):
        return super(OffsetList, self).__delitem__(key + self.offset)
    def index(self, *args):
        return super(OffsetList, self).index(*args) - self.offset

>>> a = OffsetList([1,2,3])
>>> a
[1, 2, 3]
>>> a[1]
1
>>> a.index(2)
2

Sie werden vielleicht feststellen, dass Sie auch __getslice__, __setslice__, __delslice__ implementieren wollen, ganz zu schweigen von pop und insert.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X