39

リスト内のアイテムのインデックスを見つけるには、次を使用します。

list.index(x)
Return the index in the list of the first item whose value is x. 
It is an error if there is no such item.

アイテムが見つからなかった場合にエラーがスローされるというのは、私には少し奇妙に思えます。私が(Objective-C land)から来た場合、NSNotFound列挙型(これは単なる最大整数であり、アイテムが見つからなかったことを示します)を返します。

だから私はこれを回避するために醜いものを作りました:

index = 0
for item in self.items:
   if item.id == desired_id:
        return index
    index = index + 1
 return -1

アイテムが見つからなかったことを示すために-1を使用しました。これを行うためのより良い方法は何ですか、そしてなぜPythonにはこのようなものが組み込まれていないのですか?

4

6 に答える 6

36
a = [1]
try:
    index_value = a.index(44)
except ValueError:
    index_value = -1

これはどう?

于 2012-10-31T14:48:38.633 に答える
12

-1を返すことはPythonで有効なインデックスであるため、お勧めできません(Pythonのlist.indexは、インデックスが見つからない場合に例外をスローするを参照してください)。

おそらく、インデックスエラーをキャッチし、それに応じて行動するのが最善です。

于 2012-10-31T14:48:30.083 に答える
11

指摘された一般的な解決策には同意しますが、回答とコメントで説明されたアプローチをもう少し調べて、どちらがより効率的で、どの状況であるかを確認したいと思います。

まず、3つの基本的なアプローチ:

>>> def my_index(L, obj):
...     for i, el in enumerate(L):
...             if el == obj:
...                     return i
...     return -1
... 
>>> def my_index2(L, obj):
...     try:
...             return L.index(obj)
...     except ValueError:
...             return -1
... 
>>> def my_index3(L, obj):
...     if obj in L:
...             return L.index(obj)
...     return -1
... 

1番目と2番目のソリューションはリストを1回だけスキャンするため、リストを2回スキャンするため、3番目のソリューションよりも高速であると考えるかもしれません。だから見てみましょう:

>>> timeit.timeit('my_index(L, 24999)', 'from __main__ import my_index, L', number=1000)
1.6892211437225342
>>> timeit.timeit('my_index2(L, 24999)', 'from __main__ import my_index2, L', number=1000)
0.403195858001709
>>> timeit.timeit('my_index3(L, 24999)', 'from __main__ import my_index3, L', number=1000)
0.7741198539733887

2つ目は実際に最速ですが、リストを1回だけスキャンしたとしても、1つ目は3つ目よりもはるかに遅いことに気付くでしょう。リストのサイズを大きくしても、状況はあまり変わりません。

>>> L = list(range(2500000))
>>> timeit.timeit('my_index(L, 2499999)', 'from __main__ import my_index, L', number=100)
17.323430061340332
>>> timeit.timeit('my_index2(L, 2499999)', 'from __main__ import my_index2, L', number=100)
4.213982820510864
>>> timeit.timeit('my_index3(L, 2499999)', 'from __main__ import my_index3, L', number=100)
8.406487941741943

最初のものはまだ2倍遅いです。

リストにないものを検索すると、最初の解決策では事態はさらに悪化します。

>>> timeit.timeit('my_index(L, None)', 'from __main__ import my_index, L', number=100)
19.055058002471924
>>> timeit.timeit('my_index2(L, None)', 'from __main__ import my_index2, L', number=100)
5.785136938095093
>>> timeit.timeit('my_index3(L, None)', 'from __main__ import my_index3, L', number=100)
5.46164608001709

この場合にわかるように、3番目のソリューションは2番目のソリューションよりも優れており、どちらもPythonコードよりもほぼ4倍高速です。検索が失敗する頻度に応じて、#2または#3を選択します(99%の場合、番号#2の方が適しています)。

原則として、CPython用に何かを最適化する場合は、「Cレベルで」できるだけ多くの反復を実行する必要があります。あなたの例では、forループを使用して反復することは、まさにあなたがしたくないことです。

于 2012-10-31T15:08:00.290 に答える
1

例外処理を使用して、その例外をキャッチできるように発生さlist.indexValueErrorます。

簡単な例:

In [78]: lis=[1,2,3,4]

In [79]: for i in range(-1,6):
    try:
        print lis.index(i)
    except ValueError:    
        print i,"not found"

-1 not found
0 not found
0
1
2
3
5 not found
于 2012-10-31T14:48:42.720 に答える
1

この動作には明確な理由があります。

>>> import this
...
In the face of ambiguity, refuse the temptation to guess.
...

システムが「NSNotFound」のようなオブジェクトにどのように応答するかについて明確な解釈がないため、推測を拒否する必要があり、そのための特別な機能を実装することは無意味になりました。

私がこのようなことをしようとするとどうなるか考えてみてください。

[ objective.index(i)+1 for i in reference_list ]

NSNotFoundに1を追加するとはどういう意味ですか?次のようなことをする方が簡単ではありませんか。

[ objective.index(i)+1 for i in reference_list if i in objective ]

そして、-1これは実際にはリストの有効なインデックスであり、「最後の値を取る」ことを意味します。したがって、これを特別なエラーコードとして使用しようとすると、厄介で厄介なバグが発生する可能性が非常に高くなります。

Guidoは非常に強いデザインセンスを持っています。彼を過小評価しないでください;)

それでも、そのようなものが必要な場合は、次のコードを試してみてください。

class NotFoundError(Exception):
    def __init__(self,container,index):
        self.message = "object "+str(index)+" not found on "+str(container)
        self.container = container
        self.index = index
    def __str__(self):
        return self.message

def getindex(cont,idx):
    try:
        return cont.index(idx)
    except:
        return NotFoundError(cont,idx)

a = [1,2]

print getindex(a,3)
#object 3 not found on [1, 2]
于 2012-10-31T15:08:43.177 に答える
-1

「エラーをスローする」よりも「例外を発生させる」と考える方がよいでしょう。

Pythonの例外は、エラーだけでなく、例外的な状況のためのものです。そのため、この名前が付けられています。特別な値を返した場合list.index()、それは次の値である必要があります

  1. list.index()アイテムを見つけていたら返品できなかった

  2. その後、ナイーブなコードによって誤解されることはありませんでした。

最初の条件はすべての正の整数(ゼロとを含むsys.maxint)を除外し、2番目の条件は負の整数も除外します(負のインデックスはPythonでリストにインデックスを付ける有効な方法であるため)。整数以外のものは、後続のコードがそれが取得しようとしているものであると想定している場合、とにかく後で例外を発生させる可能性があります。

メソッドが例外を発生させるか、特別な値を返すかに関係なく、多くの場合、その情報を使用して何かを行う必要があります。これは次のとおりです。

try:
    index = list.index(x)
except ValueError:
    # do something

これよりも読みやすいです:

index = list.index(x)
if index == some_special_value:
    # do something

...後者の場合、例外的な状況を防ぐのを忘れると、コードがサイレントに失敗し、コードの他の場所で混乱を招くエラーが発生する可能性があります。

さらに悪いことに、この特別な値が何であるかを覚えたり調べたりする必要があります。これと、そのように動作する他のメソッドや関数についてです。

于 2012-10-31T15:09:36.830 に答える