python - que - One-liner para verificar si un iterador produce al menos un elemento




que es un iterable en python (6)

Actualmente estoy haciendo esto:

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

Pero me gustaría una expresión que pueda colocar dentro de una simple declaración if . ¿Hay algo incorporado que haría que este código parezca menos torpe?

any() devuelve False si un iterable está vacío, pero posiblemente iterará sobre todos los ítems si no lo está. Solo lo necesito para verificar el primer artículo.

Alguien pregunta qué estoy tratando de hacer. He escrito una función que ejecuta una consulta SQL y arroja sus resultados. A veces, cuando llamo a esta función, solo quiero saber si la consulta arrojó algo y tomar una decisión basada en eso.


En Python 2.6+, si el nombre sentinel está vinculado a un valor que el iterador no puede ceder,

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

Si no tiene idea de qué podría rendir el iterador, haga su propio centinela (por ejemplo, en la parte superior de su módulo) con

sentinel = object()

De lo contrario, podría usar, en el rol de centinela, cualquier valor que "conozca" (basado en consideraciones de la aplicación) que el iterador no puede ceder.


Esto no es realmente más limpio, pero muestra una forma de empaquetarlo en una función sin pérdidas:

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

Esto no es realmente pitónico, y para casos particulares, probablemente haya soluciones mejores (pero menos generales), como el next valor predeterminado.

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

Esto no es general porque Ninguno puede ser un elemento válido en muchos iterables.


Se trata de un contenedor iterator overkill que generalmente permite verificar si hay un siguiente elemento (mediante la conversión a booleano). Por supuesto bastante ineficiente.

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)

Salida:

False
[]
True
[1, 2, 3]

Un poco tarde, pero ... Podría convertir el iterador en una lista y luego trabajar con esa lista:

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

__length_hint__ estima la longitud de la list(it) - es un método privado, sin embargo:

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

any no irá más allá del primer elemento si es verdadero. En caso de que el iterador arroje algo falso, puede escribir any(True for _ in iterator) .





iterator