Der beste Weg, um list.index (möglicherweise nicht vorhanden) in Python zu behandeln?

113

Ich habe Code, der ungefähr so ​​aussieht:

thing_index = thing_list.index(thing)
otherfunction(thing_list, thing_index)

ok also das ist vereinfacht aber du kommst auf die idee. Jetzt ist thingmöglicherweise nicht wirklich in der Liste, in diesem Fall möchte ich -1 als übergeben thing_index. In anderen Sprachen ist dies das, was Sie erwarten würden index(), wenn das Element nicht gefunden werden könnte. In der Tat wirft es ein ValueError.

Ich könnte das tun:

try:
    thing_index = thing_list.index(thing)
except ValueError:
    thing_index = -1
otherfunction(thing_list, thing_index)

Aber das fühlt sich schmutzig an und ich weiß nicht, ob ValueErrores aus einem anderen Grund angehoben werden könnte. Ich habe die folgende Lösung basierend auf Generatorfunktionen entwickelt, die jedoch etwas komplex erscheint:

thing_index = ( [(i for i in xrange(len(thing_list)) if thing_list[i]==thing)] or [-1] )[0]

Gibt es einen saubereren Weg, um dasselbe zu erreichen? Nehmen wir an, die Liste ist nicht sortiert.

Draemon
quelle
4
"... in diesem Fall möchte ich -1 als übergeben thing_index." - Das ist definitiv unpythonisch. Das Übergeben eines (bedeutungslosen) Token-Werts für den Fall, dass eine Operation nicht erfolgreich ist, ist verpönt - Ausnahmen sind hier wirklich der richtige Weg. Zumal thing_list[-1]es sich um einen gültigen Ausdruck handelt, der den letzten Eintrag in der Liste bedeutet.
Tim Pietzcker
@ Jellybean: Facepalm ... Finde den Java-Codierer: P
Draemon
4
@ Tim: Es gibt eine str.findMethode, die genau das tut: Gibt zurück, -1wenn die Nadel im Betreff nicht gefunden wird.
SilentGhost
@ Tim Keine wäre besser als ... und dies wäre analog zu dict [key] vs dict.get [key]
Draemon
@ SilentGhost: Hm, interessant. Ich muss das vielleicht genauer untersuchen. str.index()löst eine Ausnahme aus, wenn die Suchzeichenfolge nicht gefunden wird.
Tim Pietzcker

Antworten:

65

Die Verwendung der try-without-Klausel ist nicht "schmutzig". Dies ist der pythonische Weg. ValueErrorwird nur von der .indexMethode ausgelöst, da dies der einzige Code ist, den Sie dort haben!

Um den Kommentar zu beantworten:
In Python ist es gut etabliert, um Vergebung zu bitten, als um eine Erlaubnisphilosophie zu erhalten , und no index wird diese Art von Fehler für keine anderen Probleme auslösen. Nicht, dass mir irgendetwas einfallen könnte.

SilentGhost
quelle
29
Ausnahmen gelten sicherlich für Ausnahmefälle, und das ist kaum so. Ich hätte kein solches Problem, wenn die Ausnahme spezifischer als ValueError wäre.
Draemon
1
Ich weiß, dass es nur mit dieser Methode geworfen werden kann, aber wird es garantiert nur aus diesem Grund geworfen ? Nicht, dass ich mir einen anderen Grund vorstellen könnte, warum der Index fehlschlagen würde. Aber sind dann nicht Ausnahmen für genau die Dinge, an die Sie vielleicht nicht denken?
Draemon
4
Ist nicht {}.get(index, '')mehr pythonisch? Ganz zu schweigen von kürzeren besser lesbaren.
Esteban Küber
1
Ich benutze dict [key] , wenn ich die Schlüssel existieren und dict.get (key) erwarten , wenn ich nicht sicher bin, und ich bin der Suche nach hier etwas Gleichwertiges. Eine Rückgabe Noneanstelle von -1 wäre in Ordnung, aber wie Sie selbst kommentiert haben, gibt str.find () -1 zurück. Warum sollte es also keine list.find () geben, die dasselbe tut? Ich kaufe nicht das "pythonische" Argument
Draemon
3
Der Punkt ist jedoch, dass die pythonischste Lösung darin besteht, nur try / exception und überhaupt nicht den Sentinel-Wert -1 zu verwenden. IE sollten Sie umschreiben otherfunction. Auf der anderen Seite, wenn es nicht kaputt ist, ...
Andrew Jaffe
53
thing_index = thing_list.index(elem) if elem in thing_list else -1

Eine Linie. Einfach. Keine Ausnahmen.

Emil Ivanov
quelle
35
Einfach ja, aber das führt zwei lineare Suchvorgänge durch, und obwohl die Leistung per se kein Problem darstellt, scheint dies übertrieben zu sein.
Draemon
4
@Draemon: Stimmen Sie zu - das werden 2 Durchgänge ausgeführt - aber es ist unwahrscheinlich, dass dies bei einer Codebasis mit tausend Zeilen der Engpass sein wird. :) Mit kann man sich immer für eine zwingende Lösung entscheiden for.
Emil Ivanov
mit LambdindexOf = lambda item,list_ : list_.index(item) if item in list_ else -1 # OR None
Alaa Akiel
17

Der dictTyp hat eine getFunktion . Wenn der Schlüssel nicht im Wörterbuch vorhanden ist, ist das zweite Argument getder Wert, den er zurückgeben soll. Ebenso gibt es setdefault, was den Wert in der zurückgibtdict wenn der Schlüssel vorhanden ist, andernfalls wird der Wert gemäß Ihrem Standardparameter festgelegt und dann Ihr Standardparameter zurückgegeben.

Sie können den listTyp um eine getindexdefaultMethode erweitern.

class SuperDuperList(list):
    def getindexdefault(self, elem, default):
        try:
            thing_index = self.index(elem)
            return thing_index
        except ValueError:
            return default

Welches könnte dann verwendet werden wie:

mylist = SuperDuperList([0,1,2])
index = mylist.getindexdefault( 'asdf', -1 )
Ross Rogers
quelle
6

Es ist nichts falsch mit Ihrem Code, der verwendet ValueError. Hier ist noch ein Einzeiler, wenn Sie Ausnahmen vermeiden möchten:

thing_index = next((i for i, x in enumerate(thing_list) if x == thing), -1)
jfs
quelle
Ist das Python 2.6? Ich weiß, dass ich es nicht erwähnt habe, aber ich benutze 2.5. Dies ist wahrscheinlich, was ich in 2.6 tun würde
Draemon
1
@Draemon: Ja, die next()Funktion ist in Python 2.6+ vorhanden. Aber es ist einfach für 2.5 zu implementieren, siehe next () Funktionsimplementierung für Python 2.5
jfs
4

Diese Ausgabe ist eine der Sprachphilosophie. In Java zum Beispiel gab es immer die Tradition, dass Ausnahmen eigentlich nur unter "außergewöhnlichen Umständen" verwendet werden sollten, wenn Fehler aufgetreten sind, und nicht zur Flusskontrolle . Am Anfang war dies aus Leistungsgründen, da Java-Ausnahmen langsam waren, aber jetzt ist dies der akzeptierte Stil geworden.

Im Gegensatz dazu hat Python immer Ausnahmen verwendet, um den normalen Programmablauf anzuzeigen, wie z. ValueErrorB. das Erhöhen von a, wie wir hier diskutieren. Im Python-Stil ist daran nichts "schmutziges", und es gibt noch viel mehr, woher das kommt. Ein noch häufigeres Beispiel ist eine StopIterationAusnahme, die durch die next()Methode eines Iterators ausgelöst wird , um zu signalisieren, dass keine weiteren Werte vorhanden sind.

Tendayi Mawushe
quelle
Tatsächlich löst das JDK viel zu viele geprüfte Ausnahmen aus, sodass ich nicht sicher bin, ob die Philosophie tatsächlich auf Java angewendet wird. Ich habe per se kein Problem damit, StopIterationweil klar definiert ist, was die Ausnahme bedeutet. ValueErrorist nur ein bisschen zu allgemein.
Draemon
Ich bezog mich auf die Idee, dass Ausnahmen nicht für die Flusskontrolle verwendet werden sollten: c2.com/cgi/wiki?DontUseExceptionsForFlowControl , nicht so sehr die Anzahl der überprüften Ausnahmen, die Java hat, eine ganz andere Diskussion: mindview.net/Etc/Discussions / CheckedExceptions
Tendayi Mawushe
4

Wenn Sie dies häufig tun, ist es besser, es in einer Hilfsfunktion aufzubewahren:

def index_of(val, in_list):
    try:
        return in_list.index(val)
    except ValueError:
        return -1 
Veneet Reddy
quelle
4

Was ist damit?

li = [1,2,3,4,5] # create list 

li = dict(zip(li,range(len(li)))) # convert List To Dict 
print( li ) # {1: 0, 2: 1, 3: 2, 4:3 , 5: 4}
li.get(20) # None 
li.get(1)  # 0 
Alaa Akiel
quelle
1

Was ist damit:

otherfunction(thing_collection, thing)

Anstatt etwas so Implementierungsabhängiges wie einen Listenindex in einer Funktionsschnittstelle verfügbar zu machen, übergeben Sie die Sammlung und das Objekt und lassen Sie andere Funktionen die Probleme mit dem "Test auf Mitgliedschaft" behandeln. Wenn eine andere Funktion als sammlungsunabhängig geschrieben wird, beginnt sie wahrscheinlich mit:

if thing in thing_collection:
    ... proceed with operation on thing

Dies funktioniert, wenn thing_collection eine Liste, ein Tupel, eine Menge oder ein Diktat ist.

Dies ist möglicherweise klarer als:

if thing_index != MAGIC_VALUE_INDICATING_NOT_A_MEMBER:

Welches ist der Code, den Sie bereits in einer anderen Funktion haben.

PaulMcG
quelle
1

Was ist damit:

temp_inx = (L + [x]).index(x) 
inx = temp_inx if temp_inx < len(L) else -1
Jie Xiong
quelle
0

Ich habe das gleiche Problem mit der Methode ".index ()" in Listen. Ich habe kein Problem mit der Tatsache, dass es eine Ausnahme auslöst, aber ich bin absolut nicht einverstanden mit der Tatsache, dass es sich um einen nicht beschreibenden ValueError handelt. Ich könnte verstehen, ob es ein IndexError gewesen wäre.

Ich kann sehen, warum die Rückgabe von "-1" ebenfalls ein Problem darstellt, da es sich um einen gültigen Index in Python handelt. Aber realistisch gesehen erwarte ich nie, dass eine ".index ()" - Methode eine negative Zahl zurückgibt.

Hier geht ein Einzeiler (ok, es ist eine ziemlich lange Zeile ...), geht die Liste genau einmal durch und gibt "Keine" zurück, wenn der Artikel nicht gefunden wird. Es wäre trivial, es umzuschreiben, um -1 zurückzugeben, falls Sie dies wünschen.

indexOf = lambda list, thing: \
            reduce(lambda acc, (idx, elem): \
                   idx if (acc is None) and elem == thing else acc, list, None)

Wie benutzt man:

>>> indexOf([1,2,3], 4)
>>>
>>> indexOf([1,2,3], 1)
0
>>>
haavee
quelle
-2

Ich weiß nicht, warum du denken solltest, dass es schmutzig ist ... wegen der Ausnahme? Wenn Sie einen Oneliner möchten, hier ist er:

thing_index = thing_list.index(elem) if thing_list.count(elem) else -1

aber ich würde davon abraten, es zu benutzen; Ich denke, Ross Rogers Lösung ist die beste. Verwenden Sie ein Objekt, um Ihr gewünschtes Verhalten zu kapseln. Versuchen Sie nicht, die Sprache auf Kosten der Lesbarkeit an ihre Grenzen zu bringen.

Alan Franzoni
quelle
1
Ja, wegen der Ausnahme. Ihr Code führt zwei lineare Suchvorgänge durch, nicht wahr? Nicht, dass Leistung hier wirklich wichtig wäre. Die SuperDuperList-Lösung ist nett, scheint aber in dieser speziellen Situation übertrieben zu sein. Ich denke, ich werde am Ende nur die Ausnahme erwischen, aber ich wollte sehen, ob es einen saubereren (meiner ästhetischen) Weg gibt.
Draemon
@Draemon: Nun, Sie werden den Code, den Sie haben, in die find()Funktion einkapseln und alles wird sauber sein;)
SilentGhost
1
Es ist merkwürdig, dass meine Antwort zwei Gegenstimmen hat, während Emil Ivanovs, obwohl semantisch identisch, eine der am besten bewerteten ist. Höchstwahrscheinlich passiert dies, weil meine langsamer ist, da ich count () anstelle des "in" -Operators verwendet habe ... zumindest ein Kommentar, der besagt, dass das großartig gewesen wäre :-)
Alan Franzoni
-2

Ich würde vorschlagen:

if thing in thing_list:
  list_index = -1
else:
  list_index = thing_list.index(thing)
Jonas
quelle
2
Das Problem bei dieser Lösung ist, dass "-1" ein gültiger Index in der Liste ist (letzter Index; der erste vom Ende). Ein besserer Weg, dies zu handhaben, wäre die Rückgabe von False im ersten Zweig Ihrer Erkrankung.
FanaticD