2010-01-25 5 views
66

Ich habe Code, der etwa wie folgt aussieht:Der beste Weg, um list.index (möglicherweise nicht vorhanden) in Python zu behandeln?

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

ok damit vereinfacht ist, aber Sie bekommen die Idee. Jetzt ist thing möglicherweise nicht wirklich in der Liste, in diesem Fall möchte ich -1 als thing_index übergeben. In anderen Sprachen ist das, was Sie erwarten würden index() zurückzukehren, wenn es das Element nicht finden konnte. In der Tat wirft es eine ValueError.

konnte ich dies tun:

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

Aber das fühlt sich schmutzig, und ich weiß nicht, ob ValueError aus einem anderen Grund erhöht werden könnte. Ich kam mit der folgenden Lösung auf Basis von Generator-Funktionen, aber es scheint ein wenig komplex:

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

Gibt es einen sauberen Weg, um die gleiche Sache zu erreichen? Nehmen wir an, die Liste ist nicht sortiert.

+1

s/catch/except /: o) –

+4

"... wobei ich -1 als' thing_index' übergeben wollen" - Das ist definitiv unpythonisch. Das Übergeben eines (bedeutungslosen) Tokenwerts, falls eine Operation nicht erfolgreich ist, ist verpönt - Ausnahmen sind hier wirklich der richtige Weg. Zumal 'thing_list [-1]' ein gültiger Ausdruck ist, der den letzten Eintrag in der Liste bedeutet. –

+0

@jellybean: * facepalm * ... Finde den Java-Kodierer: P – Draemon

Antwort

45

Es gibt nichts "schmutzig" über die Verwendung der try-except-Klausel. Das ist der pythonische Weg. ValueError wird nur durch die .index Methode ausgelöst, weil es der einzige Code ist, den Sie dort haben!

den Kommentar zu beantworten:
In Python wird easier to ask forgiveness than to get permission Philosophie gut etabliert, und keinindex wird diese Art von Fehler für alle anderen Fragen, die nicht erhöhen. Nicht, dass mir irgendetwas einfällt.

+14

Sicher Ausnahmen sind für Ausnahmefälle, und das ist kaum das.Ich hätte kein solches Problem, wenn die Ausnahme spezifischer als ValueError wäre. – Draemon

+1

Ich weiß, dass es nur aus dieser * Methode * geworfen werden kann, aber ist es garantiert, nur aus diesem Grund * geworfen zu werden? Nicht, dass ich an einen anderen Grundindex denken könnte, würde scheitern ... aber sind dann keine Ausnahmen für genau jene Dinge, an die du vielleicht nicht denkst? – Draemon

+0

@Draemon: Deshalb überprüft es nur für 'ValueError' und nicht irgendeine Form der Ausnahme. – MAK

13

Die dict type hat eine get function. Wenn der Schlüssel nicht im Wörterbuch vorhanden ist, ist das zweite Argument zu get der Wert, den es zurückgeben sollte. Analog gibt es setdefault, die den Wert in dict zurückgibt, wenn der Schlüssel vorhanden ist, andernfalls wird der Wert entsprechend Ihrem Standardparameter festgelegt und dann wird der Standardparameter zurückgegeben.

Sie könnten den list Typ erweitern, um eine getindexdefault Methode zu haben.

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

die dann wie verwendet werden könnten:

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

Ich weiß nicht, warum Sie denken, sollte es schmutzig ist ... wegen der Ausnahme? wenn Sie einen oneliner wollen, hier ist es:

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

aber ich würde davon abraten, es zu verwenden; 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 an ihre Grenzen zu Lasten der Lesbarkeit zu stoßen.

+1

Ja, wegen der Ausnahme. Ihr Code wird zwei lineare Suchen durchführen, nicht wahr? Nicht diese Leistung ist hier wirklich wichtig. Die SuperDuperList-Lösung ist nett, scheint aber in dieser speziellen Situation wie Overkill zu sein. Ich denke, ich werde am Ende nur die Ausnahme fangen, aber ich wollte sehen, ob es eine sauberere (zu meiner ästhetischen) Art gab. – Draemon

+0

@Draemon: Nun, Sie werden den Code, den Sie haben, in die 'find()' -Funktion kapseln und es wird alles sauber;) – SilentGhost

+1

Es ist seltsam, dass meine Antwort hat zwei Downvotes, während Emil Ivanov, während semantisch identisch ist, ist derjenige der am meisten Uplooted. Wahrscheinlich passiert dies, weil meins langsamer ist, da ich count() anstelle des "in" -Operators verwendet habe ... zumindest ein Kommentar, der das großartig gewesen wäre :-) –

5

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

thing_index = next((i for i, x in enumerate(thing_list) if x == thing), -1) 
+0

Ist das Python 2.6? Ich weiß, dass ich es nicht erwähnt habe, aber ich benutze 2,5. Dies ist wahrscheinlich, was ich tun würde in 2.6 – Draemon

+1

@Draemon: Ja, 'next()' Funktion existiert in Python 2.6 +. Aber es ist einfach für 2.5 zu implementieren, siehe [next() - Funktionsimplementierung für Python 2.5] (http://stackoverflow.com/questions/500578/is-there-a-alternative-way-of-calling-next-on) -python-generators/500609 # 500609) – jfs

3

Dieses Problem ist eine der Sprachphilosophie. In Java zum Beispiel gab es schon immer eine Tradition, dass Ausnahmen wirklich nur in "außergewöhnlichen Umständen" verwendet werden sollten, wenn Fehler aufgetreten sind, anstatt für flow control.Zu Beginn war dies aus Performancegründen, da Java-Ausnahmen langsam waren, aber jetzt ist dies der akzeptierte Stil geworden.

Im Gegensatz dazu hat Python immer Ausnahmen verwendet, um einen normalen Programmablauf anzuzeigen, wie zum Beispiel die Erhöhung eines ValueError, wie wir hier diskutieren. Es gibt nichts "Schmutziges" im Python-Stil und es gibt viele weitere, woher das kommt. Ein noch gängiges Beispiel ist die durch einen StopIteration exception Iterators next() Verfahren angehoben wird, um zu signalisieren, dass es keine weiteren Werte.

+0

Eigentlich wirft das JDK * way * zu viele geprüfte Ausnahmen, daher bin ich mir nicht sicher, ob diese Philosophie tatsächlich auf Java angewendet wird. Ich habe per se kein Problem mit 'StopIteration', weil klar definiert ist, was die Ausnahme bedeutet. 'ValueError' ist nur etwas zu generisch. – Draemon

+0

Ich bezog mich auf die Idee, dass Ausnahmen nicht für die Flusskontrolle verwendet werden sollten: http://c2.com/cgi/wiki?DontUseExceptionsForFlowControl, nicht so sehr die Anzahl der geprüften Ausnahmen, die Java hat, die eine ganze andere Diskussion: http: //www.mindview.net/Etc/Discussions/CheckedExceptions –

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

Eine Zeile. Einfach. Keine Ausnahmen.

+18

Einfach ja, aber das macht zwei lineare Suchvorgänge und während Leistung kein Problem per se ist, scheint das exzessiv. – Draemon

+2

@Draemon: Zustimmen - das wird 2 Pässe - aber es ist unwahrscheinlich, dass von einer Code-Basis mit tausend Zeilen dieser wird der Engpass sein. :) Man kann sich immer für eine zwingende Lösung mit 'for' entscheiden. –

1

Was dazu:

otherfunction(thing_collection, thing) 

Anstatt etwas aussetzen so abhängig von der Implementierung wie ein Listenindex in einer Funktionsschnittstelle, die Sammlung übergeben und die Sache und läßt otherfunction Deal mit dem „Test für die Mitgliedschaft“ Fragen . Wenn otherfunction geschrieben Sammlung typunabhängig zu sein, dann wäre es wahrscheinlich beginnen mit:

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

, die funktioniert, wenn thing_collection eine Liste, Tupel, Set, oder dict.

Dies ist möglicherweise klarer als:

if thing_index != MAGIC_VALUE_INDICATING_NOT_A_MEMBER: 

, die der Code ist bereits in otherfunction haben.

-2

Ich würde vorschlagen:

if thing in thing_list: 
    list_index = -1 
else: 
    list_index = thing_list.index(thing) 
+2

Problem mit dieser Lösung ist, dass "-1" ein gültiger Index in der Liste ist (letzter Index; der erste vom Ende). Besserer Weg, um damit umzugehen, wäre die Rückkehr False im ersten Zweig Ihres Zustandes. – FanaticD

0

Ich habe das gleiche Problem mit dem ".index()" Methode auf Listen. Ich habe kein Problem mit der Tatsache, dass es eine Ausnahme auslöst, aber ich stimme der Tatsache nicht zu, dass es sich um einen nicht-deskriptiven ValueError handelt. Ich könnte verstehen, wenn es ein IndexError wäre.

ich sehen kann, warum Rückkehr „-1“ wäre zu einem Problem sein, weil es ein gültiger Index in Python ist. Aber realistisch gesehen, ich nie erwarten ein „.index()“ Methode eine negative Zahl zurück.

geht hier einen Einzeiler (ok, es ist eine ziemlich lange Schlange ...), geht durch die Liste genau einmal und kehrt „None“, wenn das Element nicht gefunden wird. Es wäre trivial, es umzuschreiben, um -1 zurückzugeben, sollten Sie es wünschen.

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

Wie verwenden:

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