述語と一致するシーケンスの最初の要素を検索します


171

述語と一致するリストの最初の要素を見つける慣用的な方法が欲しい。

現在のコードはかなり醜いです:

[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

しかし、すでに同じ機能を提供しているビルトインがある場合、このようなユーティリティ関数をコードに入力し始めることはまったく無意味です(そして、おそらくそれらがすでにそこにあることに気付かないため、時間の経過とともに繰り返される傾向があります)。


3
python sequence find function」より後で尋ねられるほか、この質問のタイトルはるかに優れいます
ウルフ

回答:


250

シーケンスseqに一致する最初の要素を見つけるにはpredicate

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

またはitertools.ifilterPython 2の場合)

next(filter(predicate, seq))

StopIteration無い場合は発生します。


そのNoneような要素がない場合に戻るには:

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

または:

next(filter(predicate, seq), None)

27
またはnext、例外を発生させる代わりに使用される2番目の「デフォルト」引数を指定できます。
Karl Knechtel、2011

2
@fortran:next()はPython 2.6以降で使用できます。新機能のページを読んで、新機能にすぐに慣れることができます。
jfs

1
私はpythonの初心者であり、ドキュメントを読み、ifilterは「yield」メソッドを使用します。これは、私たちが行くにつれて述語が遅延評価されることを意味すると思います。私は少し高価である述語の機能を持っていると私はへの唯一の反復我々はアイテムを見つけるポイントまでたいので、すなわち、私たちは、リスト全体を述語を実行してはいけない
Kannan Ekanath

2
@geekazoid:seq.find(&method(:predicate))またはインスタンスメソッドのさらに簡潔な例:[1,1,4].find(&:even?)
jfs

16
ifilterfilterPython 3でに名前が変更されました
tsauerwein 2016年

92

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

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

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

このかなり人気のある記事では、この問題についてさらに説明しています最もクリーンなPythonのリスト内検索関数?


8

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

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

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

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


それだけでなく[]、イテレータが終了しないか、その要素を作成するのが難しい場合、問題が発生する可能性があります。後で取得すると...
glglgl

6
'generator' object has no attribute 'next'Python 3で
jfs

@glglgl-最初の点については(決して終わらない)引数は有限シーケンス[より正確にはOPの質問によるリスト]であるため、疑わしい。2番目の場合と同様に、指定された引数はシーケンスであるため、この関数が呼び出されるまでにオブジェクトは既に作成および保存されているはずです。
Mac

@JFSebastian-ありがとう、知らなかった!:)好奇心から、この選択の背後にある設計原則は何ですか?
Mac

@mac-他の特別なメソッドの二重下線との一貫性のため。python.org/dev/peps/pep-3114を参照してください
チューイー

1

JF 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()と呼ばれる事前定義された関数を使用していますが、上記の例よりも改善の可能性があります:フィルター/ ifilterの使用を避けます。

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

11
うわぁ!それが原因である場合は、内部に「if」を含む単純な「for」ループを実行します-はるかに読みやすい
Nick Perkins
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.