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

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

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 라이너를 생각할 수 없습니다. 필요하지 않은 경우이 기능을 특별히 복사하고 싶지 않습니다. 조건과 일치하는 첫 번째 항목을 가져 오는 기본 제공 방법이 있습니까?



답변

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함수의 본문으로를 다양한 답변이 제안한대로. 이러한 대안들에는 부가가치가 많지 않으므로 처음 제안한 아주 간단한 버전을 사용하겠습니다.


답변

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

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


답변

젠장 예외!

나는 이 대답을 좋아 한다 . 때문에, 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


답변

을 사용하는 것과 마찬가지로 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블록 을 만들 필요가 없습니다 . 그러나 그것은 구문에 대해 모호하고 모욕적 인 것 같습니다.


답변

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)


답변

나는 이것을 쓸 것이다

next(x for x in xrange(10) if x > 3)


답변

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

from itertools import ifilter

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