반복자가 적어도 하나의 요소를 생성하는지 확인하는 한 줄?


102

현재 나는 이것을하고있다 :

try:
    something = iterator.next()
    # ...
except StopIteration:
    # ...

하지만 간단한 if문장 안에 넣을 수있는 표현을 원합니다 . 이 코드를 덜 어색하게 보이게하는 내장 기능이 있습니까?

any()Falseiterable이 비어 있으면 반환 하지만 그렇지 않은 경우 모든 항목을 잠재적으로 반복합니다. 첫 번째 항목 만 확인하면됩니다.


누군가 내가 뭘 하려는지 묻습니다. SQL 쿼리를 실행하고 그 결과를 산출하는 함수를 작성했습니다. 때때로이 함수를 호출 할 때 쿼리가 어떤 것을 반환했는지 알고이를 기반으로 결정을 내리고 싶습니다.


2
또한이 코드의 문제는 첫 번째 요소를 먹기 때문에 함수로 패키징 할 수 없다는 것입니다. 좋은 질문.
andrewrk 2010 년

2
제 경우에는 요소가 전혀 필요하지 않으며 최소한 하나의 요소가 있는지 알고 싶습니다.
Bastien Léonard

2
하! 동일한 솔루션을 찾으려고 할 때 동일한 사용 사례!
Daniel


답변:


134

anyTrue이면 첫 번째 요소를 넘어 가지 않습니다. 반복자가 잘못된 것을 산출하는 경우 any(True for _ in iterator).


이것은 re.finditer와 함께 나를 위해 일하는 것 같습니다. 첫 번째 성공에서 멈추는 것을 쉽게 테스트 할 수 있습니다. 실행 any((x > 100 for x in xrange(10000000)))한 다음 실행 any((x > 10000000 for x in xrange(100000000)))하십시오. 두 번째는 훨씬 더 오래 걸립니다.
chbrown

1
"적어도 X에서"의 경우에이 작품sum(1 for _ in itertools.islice(iterator, max_len)) >= max_len
데이브 버틀러

11
마찬가지로 반복기가 비어 있는지 확인해야하는 경우 반복기가 비어 all(False for _ in iterator)있는지 확인할 수 있습니다 . (반복자가 비어 있으면 모두 True를 반환하고 그렇지 않으면 첫 번째 False 요소를 볼 때 중지됩니다.)
KGardevoir

23
이 솔루션의 큰 문제는 그것이 비어 있지 않으면 반복기에서 반환 된 값을 실제로 사용할 수 없다는 것입니다.
Ken Williams

42

Python 2.6 이상에서 이름 sentinel이 반복자가 산출 할 수없는 값에 바인딩 된 경우

if next(iterator, sentinel) is sentinel:
    print('iterator was empty')

이터레이터가 무엇을 산출 할 수 있을지 모르는 경우 다음을 사용하여 자신의 센티넬 (예 : 모듈 상단)을 만드십시오.

sentinel = object()

그렇지 않으면 센티넬 역할에서 반복자가 산출 할 수없는 (응용 프로그램 고려 사항에 따라) "알고있는"값을 사용할 수 있습니다.


1
좋은! 내 사용 사례의 경우 if not next(iterator, None):None이 항목의 일부가 아니라고 확신했기 때문에 충분했습니다. 올바른 방향을 알려 주셔서 감사합니다!
wasabigeek

1
@wasabi not빈 목록, False 및 0과 같은 거짓 개체에 대해 True를 반환 한다는 점 을 기억하십시오 . is not None더 안전하고 제 생각에는 더 명확합니다.
Caagr98

21

이것은 정말로 깨끗하지는 않지만 무손실 함수로 패키징하는 방법을 보여줍니다.

def has_elements(iter):
  from itertools import tee
  iter, any_check = tee(iter)
  try:
    any_check.next()
    return True, iter
  except StopIteration:
    return False, iter

has_el, iter = has_elements(iter)
if has_el:
  # not empty

이것은 실제로 비단뱀 적이 지 않으며 특정 경우에는 아마도 다음 기본값 과 같은 더 나은 (그러나 덜 일반적인) 솔루션이있을 것입니다 .

first = next(iter, None)
if first:
  # Do something

None은 많은 이터 러블에서 유효한 요소가 될 수 있기 때문에 일반적이지 않습니다.


이것이 아마도 이것을하는 가장 좋은 방법 일 것입니다. 그러나 OP가 무엇을하려고하는지 아는 것이 도움이 될까요? 아마도 더 우아한 해결책이있을 것입니다 (결국 이것은 파이썬입니다).
rossipedia 2010 년

감사합니다 next()..
Bastien Léonard

1
@Bastien, 괜찮지 만 적절한 센티넬로 그렇게 하십시오 (내 대답 참조).
Alex Martelli

3
이 솔루션에는 엄청난 메모리 누수가 있습니다. teeitertools에서이 경우 원래의 반복자에서 모든 단일 요소를 유지해야합니다 any_check사전에 지금 필요합니다. 이것은 원래 반복자를 목록으로 변환하는 것보다 더 나쁩니다.
Rafał Dowgird 2011 년

1
@ RafałDowgird 이것은 원래 반복자를 목록으로 변환하는 것보다 더 나쁩니다. 사실은 아닙니다. 무한 시퀀스에 대해 생각해보십시오.
Piotr Dobrogost

6

당신이 사용할 수있는:

if zip([None], iterator):
    # ...
else:
    # ...

하지만 코드 리더에게는 약간 설명이 필요하지 않습니다.


2
.. ([None] 대신 1 개 항목 반복 가능)
mykhal

5

이를 수행하는 가장 좋은 방법은 peekablefrom more_itertools.

from more_itertools import peekable
iterator = peekable(iterator)
if iterator:
    # Iterator is non-empty.
else:
    # Iterator is empty.

이전 이터레이터에 대한 참조를 유지했다면 이터레이터가 발전 할 것이라는 점에 유의하세요. 그때부터 새로운 peekable iterator를 사용해야합니다. 그러나 실제로 peekable은 이전 반복자를 수정하는 유일한 코드 일 것으로 예상하므로 어쨌든 이전 반복기에 대한 참조를 유지해서는 안됩니다.


3

이건 어떤가요:

In [1]: i=iter([])

In [2]: bool(next(i,False))
Out[2]: False

In [3]: i=iter([1])

In [4]: bool(next(i,False))
Out[4]: True

4
흥미로운 것! 그러나 next ()가 실제로 산출 된 것이기 때문에 False를 반환하면 어떻게 될까요?
Bastien Léonard

BastienLéonard 클래스를 만들기 @ class NotSet: pass, 다음을 확인if next(i, NotSet) is NotSet: print("Iterator is empty")
Elijas

-1

__length_hint__ 길이를 추정 합니다 list(it). 그러나 개인 방법입니다.

x = iter( (1, 2, 3) )
help(x.__length_hint__)
      1 Help on built-in function __length_hint__:
      2 
      3 __length_hint__(...)
      4     Private method returning an estimate of len(list(it)).

4
모든 이터레이터에 대해 보장되지는 않습니다. >>> def it () : ... yield 1 ... yield 2 ... yield 3 ... >>> i = it () >>> i .__ length_hint__ Traceback (가장 최근 호출 마지막) : File " <stdin> ", 줄 1, <module> AttributeError : 'generator'개체에 ' length_hint ' 속성이 없습니다.
andrewrk

3
힌트 일 뿐이므로 항목이 0 개 이상인 반복기에 대해 0을 반환하는 것도 합법적 일 수 있습니다.
Glenn Maynard

-1

이것은 일반적으로 부울로의 변환을 통해 다음 항목이 있는지 확인할 수있는 과잉 반복기 래퍼입니다. 물론 비효율적입니다.

class LookaheadIterator ():

    def __init__(self, iterator):
        self.__iterator = iterator
        try:
            self.__next      = next (iterator)
            self.__have_next = True
        except StopIteration:
            self.__have_next = False

    def __iter__(self):
        return self

    def next (self):
        if self.__have_next:
            result = self.__next
            try:
                self.__next      = next (self.__iterator)
                self.__have_next = True
            except StopIteration:
                self.__have_next = False

            return result

        else:
            raise StopIteration

    def __nonzero__(self):
        return self.__have_next

x = LookaheadIterator (iter ([]))
print bool (x)
print list (x)

x = LookaheadIterator (iter ([1, 2, 3]))
print bool (x)
print list (x)

산출:

False
[]
True
[1, 2, 3]

-2

조금 늦었지만 ... 반복자를 목록으로 전환 한 다음 해당 목록으로 작업 할 수 있습니다.

# Create a list of objects but runs out the iterator.
l = [_ for _ in iterator]

# If the list is not empty then the iterator had elements; else it was empty.
if l :
    pass # Use the elements of the list (i.e. from the iterator)
else :
    pass # Iterator was empty, thus list is empty.

4
전체 목록을 열거하므로 비효율적입니다. 무한 발전기에서는 작동하지 않습니다.
becko dec

@becko : 동의합니다. 그러나 원래 질문에는 그렇지 않은 것 같습니다.
Jens

3
또 다른 문제는 반복기가 메모리 오버플로를 초래할 수있는 무한한 양의 객체를 생성 할 수 있다는 것과 프로그램이 다음 명령문에 도달하지 않는다는 사실입니다
Willem Van Onsem
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.