술어와 일치하는 순서로 첫 번째 요소 찾기


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
" 파이썬 시퀀스 찾기 기능 " 이후에 질문을받는 것 외에도이 질문은 훨씬 더 나은 제목을 가지고 있습니다.
Wolf

답변:


249

순서 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예외를 발생시키는 대신 사용되는 두 번째 "기본"인수를 제공 할 수 있습니다 .
Karl Knechtel

2
@fortran : next()은 Python 2.6부터 사용 가능 합니다. 새로운 기능 페이지 를 읽어 새로운 기능에 빠르게 익숙해 질 수 있습니다.
jfs

1
나는 파이썬 초보자이며 문서를 읽고 ifilter는 "yield"메소드를 사용합니다. 이것은 우리가 갈 때 술어가 게으르게 평가된다는 것을 의미한다고 가정합니다. 즉, 약간 비싼 술어 함수가 있고 항목을 찾을 때까지만 반복하고 싶기 때문에 전체 목록을 통해 술어를 실행하지 않습니다.
Kannan Ekanath

2
@geekazoid : seq.find(&method(:predicate))또는 예를 들어 메소드에 대한 더 간결한 예 :[1,1,4].find(&:even?)
jfs

16
ifilterfilter파이썬 3에서 로 이름이 변경되었습니다 .
tsauerwein

92

기본값으로 생성기 표현식을 사용한 후 다음을 사용할 next수 있습니다.

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

이 one-liner의 경우 Python> = 2.6을 사용해야합니다.

이 인기있는 기사는이 문제에 대해 더 자세히 설명합니다. 가장 깨끗한 Python 찾기 기능? .


8

귀하의 질문에 제안한 솔루션에 문제가 있다고 생각하지 않습니다.

내 자신의 코드에서는 다음과 같이 구현합니다.

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

with 구문은을 ()사용하여 모든 목록을 한 번에 생성하는 것보다 효율적인 생성기 를 만듭니다 [].


그리고 그것뿐만 아니라 – []반복자가 끝나지 않거나 그 요소를 만들기가 어렵다면 나중에 문제가
생길 수 있습니다

6
'generator' object has no attribute 'next'Python 3에서.
jfs

@glglgl-첫 번째 점은 끝이 아닙니다. 논쟁은 유한 순서입니다 (더 정확하게 OP의 질문에 따른 목록). 두 번째로 : 다시, 제공된 인수가 시퀀스이기 때문에이 함수가 호출 될 때까지 객체가 이미 생성되어 저장되어 있어야합니다 .... 또는 뭔가 빠졌습니까?

@JFSebastian-고마워요, 몰랐어요! :) 호기심에서이 선택의 배경이되는 디자인 원칙은 무엇입니까?

@mac-다른 특수 메소드의 이중 밑줄과 일관성을 유지합니다. 참조 python.org/dev/peps/pep-3114
츄이

1

JF Sebastian의 대답은 가장 우아하지만 포트란이 지적한 것처럼 Python 2.6이 필요합니다.

파이썬 버전 <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()

11
이케! 그것으로 오면, 나는 "if"안에 간단한 "for"루프를 할 것입니다 – 훨씬 쉽게 읽을 수 있습니다
Nick Perkins
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.