조건과 일치하는 iterable에서 첫 번째 항목을 가져옵니다.


303

조건과 일치하는 목록에서 첫 번째 항목을 가져오고 싶습니다. 결과 메소드가 전체 목록을 처리하지 않는 것이 중요합니다. 이는 상당히 클 수 있습니다. 예를 들어 다음 기능이 적합합니다.

def first(the_iterable, condition = lambda x: True):
    for i in the_iterable:
        if condition(i):
            return i

이 함수는 다음과 같이 사용될 수 있습니다 :

>>> first(range(10))
0
>>> first(range(10), lambda i: i > 3)
4

그러나 나는 이것을 할 수있는 좋은 내장 / 1 라이너를 생각할 수 없습니다. 필요하지 않은 경우이 기능을 특별히 복사하고 싶지 않습니다. 조건과 일치하는 첫 번째 항목을 가져 오는 기본 제공 방법이 있습니까?


답변:


476

Python 2.6 이상에서 :

StopIteration일치하는 요소가없는 경우 발생 시키 려면 다음 을 수행하십시오.

next(x for x in the_iterable if x > 3)

당신이 원하는 경우 default_value(예를 들어 None) 대신에 반환되는 :

next((x for x in the_iterable if x > 3), default_value)

이 경우 생성기 표현식 주위에 여분의 괄호 쌍이 필요합니다. 생성기 표현식이 유일한 인수가 아닐 때마다 필요합니다.

나는 대부분의 대답이 next내장을 무시하고 있다는 것을 알기 때문에 신비한 이유 때문에 파이썬 버전 문제를 언급하지 않고 버전 2.5 이상에 100 % 집중한다고 가정합니다 (그러나 그 언급은 보이지 않습니다. 답변 할 수는 언급 next내장 내가 필요 대답 자신을 제공하기 위해 생각하는 이유 인 -은 "올바른 버전의"문제가 기록에이 방법을 ;-) 얻을 적어도합니다.

2.5에서는 반복자가 즉시 완료되면 (즉, 사용 사례의 경우 반복 가능한 항목이 조건을 만족하지 않는 경우) .next()반복자 의 방법이 즉시 증가 StopIteration합니다. 당신이 신경 쓰지 않는다면 (즉, 적어도 하나의 만족스러운 아이템 이 있어야한다는 것을 알고 있다면 ) 그냥 사용하십시오 .next()(genexp에서 최상, nextPython 2.6 의 내장 라인 이상 ).

당신이 경우에 당신이 먼저 Q에 표시 한대로 기능의 관리, 포장 일을 가장 좋은 것, 그리고 당신이 제안한 기능 구현이 잘하는 동안, 당신은 선택적으로 사용할 수 itertools하는 for...: break루프, 또는 genexp, 또는 try/except StopIteration함수의 본문으로를 다양한 답변이 제안한대로. 이러한 대안들에는 부가가치가 많지 않으므로 처음 제안한 아주 간단한 버전을 사용하겠습니다.


6
설명한대로 작동하지 않습니다. 그것은 제기 StopIteration하는 요소를 찾을 수 없을 때
SUOR

이것이 검색 결과에 나오기 때문에 2011 년 @Suor의 의견을 따랐으며 첫 번째 단락을 좀 더 명확하게 설명했습니다. 필요한 경우 계속해서 편집 내용을 수정하십시오.
코스

4
이것이 선택된 답변이므로 여기 에서 첫 번째 요소를 올바르게 선택하는 것에 대한 답변을 공유해야한다고 생각합니다 . 요컨대 다음 사용을 권장하지 않습니다.
guyarad

1
@ guyarad 그 대답에서 제안 된 솔루션은 다음을 사용하는 것보다 덜 "암호 적"입니까? 다음 답변에 대한 유일한 주장은 예외를 처리해야한다는 것입니다. 정말 ?
Abraham TS

내 의견은 내가 의견을 쓴 시간과 약간 다릅니다. 너의 의도를 알 겠어. 그것은 다루어야하는 StopIteration것이 실제로 예쁘지 않다는 말입니다. 더 나은 방법을 사용하십시오.
guyarad

29

재사용 가능하고 문서화되고 테스트 된 기능

def first(iterable, condition = lambda x: True):
    """
    Returns the first item in the `iterable` that
    satisfies the `condition`.

    If the condition is not given, returns the first item of
    the iterable.

    Raises `StopIteration` if no item satysfing the condition is found.

    >>> first( (1,2,3), condition=lambda x: x % 2 == 0)
    2
    >>> first(range(3, 100))
    3
    >>> first( () )
    Traceback (most recent call last):
    ...
    StopIteration
    """

    return next(x for x in iterable if condition(x))

기본 인수가있는 버전

@zorf는 iterable이 비어 있거나 조건과 일치하는 항목이없는 경우 미리 정의 된 반환 값을 가질 수있는이 기능의 버전을 제안했습니다.

def first(iterable, default = None, condition = lambda x: True):
    """
    Returns the first item in the `iterable` that
    satisfies the `condition`.

    If the condition is not given, returns the first item of
    the iterable.

    If the `default` argument is given and the iterable is empty,
    or if it has no items matching the condition, the `default` argument
    is returned if it matches the condition.

    The `default` argument being None is the same as it not being given.

    Raises `StopIteration` if no item satisfying the condition is found
    and default is not given or doesn't satisfy the condition.

    >>> first( (1,2,3), condition=lambda x: x % 2 == 0)
    2
    >>> first(range(3, 100))
    3
    >>> first( () )
    Traceback (most recent call last):
    ...
    StopIteration
    >>> first([], default=1)
    1
    >>> first([], default=1, condition=lambda x: x % 2 == 0)
    Traceback (most recent call last):
    ...
    StopIteration
    >>> first([1,3,5], default=1, condition=lambda x: x % 2 == 0)
    Traceback (most recent call last):
    ...
    StopIteration
    """

    try:
        return next(x for x in iterable if condition(x))
    except StopIteration:
        if default is not None and condition(default):
            return default
        else:
            raise

6
메소드로 랩핑하는 경우 최소한 StopIteration을 잡고 EmptySequence 오류를 발생 시키십시오. 요소가 없으면 훨씬 더 예쁘다.
guyarad

@guyarad 그런 종류의 ValueError입니까?
Caridorc

2
@guyarad StopIteration는 파이썬에서 정식 "요소 부족"예외입니다. 던져지는 데 문제가 없습니다. 함수에 기본 매개 변수로 전달 될 수있는 기본값 인 "없음"을 사용했을 것입니다.
Baldrickk

1
Baldrickk 나는 이것이 반복 방법이 아니라고 생각합니다. 이터레이터의 경연 대회에서는 이것을 호출하지 않습니다. 그러나 나는 그것에 대해 너무 강하게 느끼지 않습니다 :)
guyarad

1
선택적 기본 인수가 있어야하며 해당 인수를 제공하지 않으면 시퀀스의 요소가 조건을 만족하지 않는 경우에만 예외가 발생합니다.
Zorf

28

젠장 예외!

나는 이 대답을 좋아 한다 . 때문에, next()레이즈 StopIteration항목이 없을 경우 예외를, 나는 예외를 방지하기 위해 다음 코드를 사용합니다 :

a = []
item = next((x for x in a), None)

예를 들어

a = []
item = next(x for x in a)

StopIteration예외를 제기합니다 .

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

13

을 사용하는 것과 마찬가지로 ifilter생성기 표현식을 사용할 수 있습니다.

>>> (x for x in xrange(10) if x > 5).next()
6

어떤 경우 StopIteration에도 조건을 만족하는 요소가없는 경우를 대비 하여 잡을 수 있습니다.

기술적으로 말하면, 당신이 이런 식으로 할 수 있다고 생각합니다.

>>> foo = None
>>> for foo in (x for x in xrange(10) if x > 5): break
... 
>>> foo
6

try/except블록 을 만들 필요가 없습니다 . 그러나 그것은 구문에 대해 모호하고 모욕적 인 것 같습니다.


+1 : 모호하거나 모욕하지 않습니다. 모든 것을 고려하면 마지막 것은 꽤 깨끗해 보입니다.
S.Lott

6
마지막 for foo in genex: break방법은 foo = next(genex)전혀 깨끗하지 않습니다. 할당을 명확하게하지 않고 작업이 이해되지 않는 경우 발생할 수있는 예외를 제외 하고 수행하는 방법 일뿐 입니다. 예외를 잡는 대신 실패 코드로 끝나는 것은 일반적으로 파이썬에서 나쁜 것입니다.
Mike Graham

13

Python 3에서 가장 효율적인 방법은 다음 중 하나입니다 (유사한 예제 사용).

"이해" 스타일 :

next(i for i in range(100000000) if i == 1000)

경고 : 표현식은 Python 2에서도 작동하지만 rangePython 2와 같은 목록 대신 Python 3에서 iterable 객체를 반환하는 예제가 사용됩니다 (Python 2에서 iterable을 생성하려면 xrange대신 사용하십시오).

표현식은 이해 표현식에서 목록을 구성하지 않도록합니다. next([i for ...])이로 인해 요소를 필터링하기 전에 모든 요소가 포함 된 목록을 작성하고 반복을 한 번 중지하는 대신 전체 옵션을 처리해야합니다 i == 1000.

"기능" 스타일 :

next(filter(lambda i: i == 1000, range(100000000)))

경고 :이 심지어 교체, 파이썬 2 일을하지 않습니다 range와 함께 xrange그 때문에 filter대신 (비효율적)는 반복자의 목록을 작성하고 next기능은 반복자와 함께 작동합니다.

기본값

다른 응답에서 언급했듯이 next조건이 충족되지 않을 때 발생하는 예외를 피 하려면 함수에 추가 매개 변수를 추가해야합니다 .

"기능" 스타일 :

next(filter(lambda i: i == 1000, range(100000000)), False)

"이해" 스타일 :

이 스타일을 사용하면 다음 ()을 피하기 위해 이해 표현을 둘러싸 야합니다 SyntaxError: Generator expression must be parenthesized if not sole argument.

next((i for i in range(100000000) if i == 1000), False)


6

itertools모듈에는 반복자를위한 필터 기능이 있습니다. 필터링 된 반복자의 첫 번째 요소는 다음을 호출 next()하여 얻을 수 있습니다 .

from itertools import ifilter

print ifilter((lambda i: i > 3), range(10)).next()

2
생성기 표현식이 더 간단합니다.
Eric O Lebigot

1
( i) filter및 ( i) map는 적용되는 함수가 이미 존재하는 경우에 의미가있을 수 있지만 이와 같은 상황에서는 생성기 표현식을 사용하는 것이 훨씬 더 합리적입니다.
Mike Graham

이것이 가장 좋은 대답입니다. 목록 이해를 피하십시오 xahlee.info/comp/list_comprehension.html
mit

6

다음 내장이 존재하지 않는 이전 버전의 Python의 경우 :

(x for x in range(10) if x > 3).next()

5

사용하여

(index for index, value in enumerate(the_iterable) if condition(value))

하나는 확인할 수 있습니다 조건 의 첫 번째 항목의 the_iterable를 , 그 취득 인덱스 의 항목을 모두 평가해야 할 필요가없이 the_iterable .

사용할 완전한 표현은

first_index = next(index for index, value in enumerate(the_iterable) if condition(value))

여기서 first_index 는 위에서 설명한 식에서 식별 된 첫 번째 값을 가정합니다.


4

이 질문에는 이미 큰 답변이 있습니다. OP와 매우 비슷한 내 문제에 대한 해결책을 찾으려고 여기에 도착했기 때문에 2 센트 만 추가하고 있습니다.

생성기를 사용하여 기준과 일치하는 첫 번째 항목의 INDEX를 찾으려면 다음을 수행하면됩니다.

next(index for index, value in enumerate(iterable) if condition)


0

argwhereNumpy에서도이 기능을 사용할 수 있습니다 . 예를 들면 다음과 같습니다.

i) "helloworld"에서 첫 번째 "l"을 찾으십시오.

import numpy as np
l = list("helloworld") # Create list
i = np.argwhere(np.array(l)=="l") # i = array([[2],[3],[8]])
index_of_first = i.min()

ii) 0.1보다 큰 첫 번째 난수 찾기

import numpy as np
r = np.random.rand(50) # Create random numbers
i = np.argwhere(r>0.1)
index_of_first = i.min()

iii) 0.1보다 큰 마지막 난수 찾기

import numpy as np
r = np.random.rand(50) # Create random numbers
i = np.argwhere(r>0.1)
index_of_last = i.max()

-1

파이썬 3에서 :

a = (None, False, 0, 1)
assert next(filter(None, a)) == 1

파이썬 2.6에서 :

a = (None, False, 0, 1)
assert next(iter(filter(None, a))) == 1

편집 : 나는 그것이 명백하다고 생각했지만 분명히 그렇지는 않습니다. 대신 조건을 확인 None하면서 함수 (또는 lambda)를 전달할 수 있습니다 .

a = [2,3,4,5,6,7,8]
assert next(filter(lambda x: x%2, a)) == 3

-3

짧막 한 농담:

thefirst = [i for i in range(10) if i > 3][0]

기준에 따라 어떤 요소가 유효한지 확실하지 않으면 을 올릴 수 try/except있으므로이를 묶어야 [0]합니다 IndexError.


TypeError : 'generator'개체를 설명 할 수 없습니다
Josh Lee

내 나쁜, 발전기가 아닌 목록 이해력이 있어야합니다. 고마워요! :)
Mizipzor

2
전체 iterable을 평가할 이유가 없습니다 (가능하지 않을 수도 있음). 제공된 다른 솔루션 중 하나를 사용하는 것이보다 강력하고 효율적입니다.
Mike Graham
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.