web-dev-qa-db-ja.com

述語に一致するシーケンスの最初の要素を見つける

述語に一致するリストの最初の要素を見つける慣用的な方法が必要です。

現在のコードは非常にいです:

[x for x in seq if predicate(x)][0]

私はそれを変更することを考えました:

from itertools import dropwhile
dropwhile(lambda x: not predicate(x), seq).next()

しかし、もっとエレガントなものが必要です...そして、一致するものが見つからない場合に例外を発生させるのではなく、None値を返すといいでしょう。

次のような関数を定義できることはわかっています。

def get_first(predicate, seq):
    for i in seq:
        if predicate(i): return i
    return None

しかし、既に同じ機能を提供する組み込み関数がある場合、このようなユーティリティ関数でコードを埋め始めるのはまったく無味です(そして、人々はおそらくそれらが既にそこにあることに気付かないでしょうから、時間とともに繰り返される傾向があります)。

146
fortran

next(x for x in seq if predicate(x))

存在しない場合は、StopIterationを発生させます。

next(ifilter(predicate, seq), None)

そのような要素がない場合は、Noneを返します。

207
jfs

デフォルト値でジェネレーター式を使用し、next itを使用できます。

next((x for x in seq if predicate(x)), None)

このワンライナーでは、Python> = 2.6を使用する必要があります。

このかなり人気のある記事では、この問題についてさらに詳しく説明しています: Cleanest Python find-in-list function?

85
Chewie

あなたがあなたの質問で提案したどちらの解決策にも問題はないと思います。

私自身のコードでは、次のように実装します。

(x for x in seq if predicate(x)).next()

()を使用した構文は、[]を使用してすべてのリストを一度に生成するよりも効率的なジェネレーターを作成します。

5
mac

J.F. Sebastianの答えは最もエレガントですが、Fortranが指摘したようにpython 2.6が必要です。

Pythonバージョン<2.6の場合、以下が最良の方法です。

from itertools import repeat,ifilter,chain
chain(ifilter(predicate,seq),repeat(None)).next()

あるいは、後でリストが必要な場合(リストはStopIterationを処理します)、または最初のリストだけでなくすべてではない場合は、isliceでそれを行うことができます。

from itertools import islice,ifilter
list(islice(ifilter(predicate,seq),1))

更新:私は個人的に、StopIterationをキャッチしてNoneを返すfirst()と呼ばれる定義済みの関数を使用していますが、上記の例に対する改善の可能性があります:filter/ifilterの使用を避けます:

from itertools import islice,chain
chain((x for x in seq if predicate(x)),repeat(None)).next()
1
parity3