Pythonでlist.index(might-not-exist)を処理する最良の方法?


113

次のようなコードがあります。

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

わかりましたので、それは簡略化されていますが、あなたはアイデアを得ます。現在thing、実際にはリストにない場合がありthing_indexます。その場合、-1をとして渡します。他の言語では、これはindex()要素が見つからなかった場合に返されるはずです。実際にはをスローしValueErrorます。

私はこれを行うことができます:

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

しかし、これは汚い感じValueErrorがします。さらに、他の理由で発生する可能性があるかどうかはわかりません。ジェネレーター関数に基づいて次の解決策を考え出しましたが、少し複雑に見えます。

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

同じことを達成するためのより明確な方法はありますか?リストがソートされていないと仮定しましょう。


4
「...その場合、-1をとして渡したいthing_index」-これは間違いなくPythonicではありません。操作が成功しない場合に(無意味な)トークン値を渡すことは避けられます-例外は本当にここでは正しい方法です。特にthing_list[-1]は有効な式なので、リストの最後のエントリを意味します。
Tim Pietzcker、2010年

@jellybean:facepalm ... Javaコーダーを
見つける

4
@Tim:それをstr.find正確に実行するメソッドがあります-1。件名に針が見つからないときに戻ります。
SilentGhost 2010年

@Tim Noneの方が良いでしょう...そして、これはdict [key]とdict.get [key]に
似てい

@SilentGhost:うーん、面白い。私はこれをもっと詳しく調べなければならないかもしれません。str.index()検索文字列が見つからない場合は例外をスローします。
Tim Pietzcker、2010年

回答:


65

try-except句の使用については、「汚い」ことは何もありません。これはpythonicの方法です。ValueErrorが提起され.index、それはあなたがそこに持っている唯一のコードなので、唯一の方法!

コメントに答えるために:
でのPythonを、許可を取得するよりも、許しを頼むが容易哲学は十分に確立されていない、と何が index任意の他の問題のため、このタイプのエラーは発生しません。何でも思いつくわけではない。


29
確かに例外は例外的なケースであり、これはほとんどそうではありません。例外がValueErrorよりも具体的である場合は、このような問題は発生しません。
ドラえもん2010年

1
私はそれがそのメソッドからのみスローできることを知っていますが、その理由でのみスローされることが保証されていますか?インデックスが失敗する別の理由を考えることはできませんが、考えられない可能性のある例外はありませんか?
ドラえもん2010年

4
{}.get(index, '')もっとpythonicではないですか?言うまでもなく、もっと読みやすくなっています。
EstebanKüber10年

1
私はよく分からないとき、キーが存在し、dict.get(キー)を期待するとき、私はdictの[キー]を使用して、私は午前ここで何か相当探しています。None-1の代わりに返すことは問題ありませんが、あなたがコメントしたように、str.find()は-1を返します。私は「pythonic」引数を購入していません
ドラえもん

3
しかし、要点は、最もpythonicな解決策は、try / except のみを使用し、-1センチネル値をまったく使用しないことです。IE書き換える必要がありますotherfunction。一方、それが壊れていなければ、...
Andrew Jaffe

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

1行。シンプル。例外なく。


35
単純ですが、2つの線形検索を実行します。パフォーマンス自体は問題ではありませんが、それは過剰に見えます。
ドラえもん2010年

4
@Draemon:同意します-2つのパスを実行します-しかし、1000行のコードベースからこれがボトルネックになることはまずありません。:)を使用して、常に命令型ソリューションをオプトインできますfor
Emil Ivanov、2010年

lambd付きindexOf = lambda item,list_ : list_.index(item) if item in list_ else -1 # OR None
Alaa

17

dictタイプがありgetファンクションキーが辞書に存在しない場合、第2引数には、getそれは返すべきであるという値です。同様にsetdefault、がありdict、キーが存在する場合はの値を返します。それ以外の場合は、デフォルトのパラメータに従って値を設定し、デフォルトのパラメータを返します。

listタイプを拡張してgetindexdefaultメソッドを持つことができます。

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

その後、次のように使用できます。

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

6

を使用するコードに問題はありませんValueError。例外を回避したい場合は、さらに別のワンライナーを以下に示します。

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

それはPython 2.6ですか?言及しなかったのはわかっていますが、2.5を使用しています。これはおそらく私が2.6でやろうとしていることです
Draemon

1
@Draemon:はい、next()関数はPython 2.6以降に存在します。しかし、2.5の実装は簡単です
。Python2.5の

4

この問題は言語哲学の1つです。たとえばJavaでは、例外は実際には「例外的な状況」、つまりフロー制御ではなくエラーが発生した場合にのみ使用されるべきであるという伝統があります。最初はJavaの例外が遅いためこれはパフォーマンス上の理由でしたが、今ではこれが受け入れられるスタイルになっています。

対照的に、Pythonは常に例外を使用して、ValueErrorここで説明しているようにaを発生させるなど、通常のプログラムフローを示します。Pythonスタイルには、これに関する「汚い」ものは何もありません。また、それがどこから来たのかは他にもたくさんあります。さらに一般的な例は、イテレータのメソッドによってStopIteration発生する例外next()、それ以上の値がないことを通知します。


実際には、JDKはスローな方法私がいないことを確認哲学が実際のJavaに適用されていることですので、あまりにも多くのチェック例外を。StopIteration例外の意味が明確に定義されているので、それ自体には問題はありません。ValueError少し一般的すぎます。
ドラえもん2010年

私は、例外は、フロー制御のために使用すべきではないという考えに言及していました:c2.com/cgi/wiki?DontUseExceptionsForFlowControl、そんなにJavaは全体の他の議論があることを確認した例外の数:mindview.net/Etc/Discussionsを/ CheckedExceptions
Tendayi Mawushe 2010年

4

これを頻繁に実行している場合は、ヘルパー関数に格納することをお勧めします。

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

4

これはどうですか😃:

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 

1

これはどうですか:

otherfunction(thing_collection, thing)

関数インターフェイスのリストインデックスのように実装に依存するものを公開するのではなく、コレクションとThingを渡して、他の関数に「メンバーシップのテスト」の問題を処理させます。他の関数がコレクション型にとらわれないように記述されている場合、おそらく次のように始まります。

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

これは、thing_collectionがリスト、タプル、セット、または辞書の場合に機能します。

これはおそらく以下より明確です:

if thing_index != MAGIC_VALUE_INDICATING_NOT_A_MEMBER:

これは、すでに他の関数に含まれているコードです。



0

リストの ".index()"メソッドにも同じ問題があります。例外をスローするという事実には問題はありませんが、説明的でないValueErrorであるという事実には強く反対します。しかし、それがIndexErrorになるかどうかは理解できました。

「-1」を返すことがPythonで有効なインデックスであるため、これも問題になる理由がわかります。しかし、現実的に、「。index()」メソッドが負の数を返すことを期待していません

ここでは、1つのライナー(大丈夫ですが、かなり長い行です...)を実行し、リストを1回だけ通過し、アイテムが見つからない場合は「なし」を返します。必要に応じて、-1を返すように書き換えるのは簡単です。

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

使い方:

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

-2

なぜ汚いと思うべきなのかわからない…例外のため?ワンライナーが必要な場合は、次のとおりです。

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

しかし、私はそれを使用しないことをお勧めします。Ross Rogersソリューションが最良であると思います。オブジェクトを使用して、あなたの望まない行動をカプセル化します。読みやすさを犠牲にして、言語を限界まで押し出そうとしないでください。


1
はい、例外です。あなたのコードは2つの線形検索を行いますね?ここではパフォーマンスはそれほど重要ではありません。SuperDuperListソリューションは優れていますが、この特定の状況ではやり過ぎのようです。最終的には例外をキャッチすることになると思いますが、(私の美学にとって)よりクリーンな方法があるかどうかを確認したいと思いました。
ドラえもん2010年

@Draemon:まああなたはfind()関数にあなたが持っているコードをカプセル化し、それはすべてき​​れいです;)
SilentGhost

1
私の回答には2つの反対票があるのは奇妙ですが、エミールイワノフは意味的に同一ですが、最も賛成票の1つです。鉱山が遅いためおそらくこれは、代わりに:-)もののそれは、偉大だったでしょうと言って、少なくともコメント...演算子「の」Iの就業数()以来、起こる
アランFranzoni

-2

私はお勧めします:

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

2
このソリューションの問題は、 "-1"がリスト内の有効なインデックスであることです(最後のインデックス。最初から最後まで)。これを処理するより良い方法は、条件の最初のブランチでFalseを返すことです。
FanaticD 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.