Однострочный, чтобы проверить, дает ли итератор хотя бы один элемент?

В настоящее время я делаю это:

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

Но я хотел бы выразить выражение, которое я могу разместить внутри простого оператора if. Есть ли что-нибудь встроенное, которое сделает этот код менее неуклюжим?

any() возвращает False, если итерабельность пуста, но она будет потенциально перебирать все элементы, если это не так. Мне нужно только проверить первый элемент.


Кто-то спрашивает, что я пытаюсь сделать. Я написал функцию, которая выполняет SQL-запрос и дает результаты. Иногда, когда я вызываю эту функцию, я просто хочу знать, вернул ли запрос что-нибудь и принял решение на основе этого.

Ответ 1

any не будет превышать первый элемент, если он True. Если итератор дает что-то ложное, вы можете написать any(True for _ in iterator).

Ответ 2

В Python 2.6+, если имя sentinel привязано к значению, которое не может выполнить итератор,

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

Если вы не знаете, что итератор может уступить, сделайте свой собственный сторожевой (например, в верхней части вашего модуля) с помощью

sentinel = object()

В противном случае вы можете использовать в роли сторожевого устройства любое значение, которое вы "знаете" (на основе соображений приложения), которое итератор не может дать.

Ответ 3

Это не очень чистый, но он показывает способ пакетного преобразования в функцию без потерь:

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

Это не действительно pythonic, и для отдельных случаев есть, вероятно, лучшие (но менее общие) решения, такие как next по умолчанию.

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

Это не общий, потому что None может быть допустимым элементом во многих итерациях.

Ответ 4

вы можете использовать:

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

но это немного неэффективно для считывателя кода

Ответ 5

__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)).

Ответ 6

Это оболочка итератора overkill, которая обычно позволяет проверить, есть ли следующий элемент (через преобразование в логическое). Конечно, довольно неэффективно.

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]

Ответ 7

Немного поздно, но... Вы можете включить итератор в список, а затем работать с этим списком:

# 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.