hasNext nos iteradores do Python?


Respostas:


106

Não, não existe esse método. O final da iteração é indicado por uma exceção. Veja a documentação .


71
"É mais fácil pedir perdão do que permissão".

118
"É mais fácil pedir perdão do que permissão.": Verificar se um iterador tem um próximo elemento não está pedindo permissão. Há situações em que você deseja testar a existência de um próximo elemento sem consumi-lo. Eu aceitaria a solução try catch se houvesse um unnext()método para colocar o primeiro elemento de volta depois de verificar se ele existe chamando next().
Giorgio

15
@Giorgio, não há como saber se existe outro elemento sem executar o código que o gera (você não sabe se o gerador será executado yieldou não). Obviamente, não é difícil escrever um adaptador que armazene o resultado next()e forneça has_next()e move_next().
avakar

5
A mesma idéia pode ser usada para implementar o hasNext()método (produzir, armazenar em cache e retornar verdadeiro com êxito ou falso com falha). Em seguida, ambos hasNext()e next()dependeriam de um getNext()método subjacente comum e de um item em cache. Realmente não vejo por next()que não deveria estar na biblioteca padrão se é tão fácil implementar um adaptador que o fornece.
Giorgio

3
@LarsH: Você quer dizer, por exemplo, um iterador que lê de um arquivo que pode ser alterado durante a leitura? Concordo que isso pode ser um problema (que afeta qualquer método next()e fornecimento de biblioteca hasNext(), não apenas uma hipotética biblioteca Python). Então, sim, next()e hasNext()se torna complicado se o conteúdo do fluxo que está sendo verificado depende de quando os elementos são lidos.
Giorgio

239

Há uma alternativa para o StopIterationusando next(iterator, default_value).

Para exapmle:

>>> a = iter('hi')
>>> print next(a, None)
h
>>> print next(a, None)
i
>>> print next(a, None)
None

Portanto, você pode detectar Noneou outro valor pré-especificado para o final do iterador, se não desejar a exceção.


70
se você usar None como "sentinela", verifique se o seu iterador não possui nenhum Nones. Você também pode fazer sentinel = object()e next(iterator, sentinel)e teste com is.
sam boosalis

1
seguinte @samboosalis eu preferiria usar construído-in unittest.mock.sentinelobjeto que permite que você escreva uma explícita next(a, sentinel.END_OF_ITERATION)e depoisif next(...) == sentinel.END_OF_ITERATION
ClementWalter

este é mais bonita do que a exceção
datdinhquoc

O problema é que, dessa forma, você CONSUME o próximo valor do iterador também. O hasNext em Java não consome o próximo valor.
Alan Franzoni 17/07

39

Se você realmente precisa de uma has-nextfuncionalidade (porque você está apenas fielmente transcrever um algoritmo de uma implementação de referência em Java, por exemplo, ou porque você está escrevendo um protótipo que vai precisar ser facilmente transcrito para Java quando é terminado), é fácil obtenha-o com uma pequena classe de wrapper. Por exemplo:

class hn_wrapper(object):
  def __init__(self, it):
    self.it = iter(it)
    self._hasnext = None
  def __iter__(self): return self
  def next(self):
    if self._hasnext:
      result = self._thenext
    else:
      result = next(self.it)
    self._hasnext = None
    return result
  def hasnext(self):
    if self._hasnext is None:
      try: self._thenext = next(self.it)
      except StopIteration: self._hasnext = False
      else: self._hasnext = True
    return self._hasnext

agora algo como

x = hn_wrapper('ciao')
while x.hasnext(): print next(x)

emite

c
i
a
o

como requerido.

Observe que o uso de next(sel.it)como embutido requer Python 2.6 ou superior; se você estiver usando uma versão mais antiga do Python, use self.it.next()(e da mesma forma next(x)no exemplo de uso). [[Você pode razoavelmente achar que esta nota é redundante, já que o Python 2.6 existe há mais de um ano - mas mais frequentemente do que não quando eu uso os recursos do Python 2.6 em uma resposta, algum comentarista ou outro se sente obrigado a apontar que eles são recursos do 2.6, então estou tentando impedir esses comentários pela primeira vez ;-)]]


9
"Transcrever fielmente um algoritmo de uma implementação de referência em Java" é a pior razão para precisar de um has_nextmétodo. O design do Python torna impossível, digamos, usar filterpara verificar se uma matriz contém um elemento correspondente a um determinado predicado. A arrogância e a miopia da comunidade Python são impressionantes.
Jonathan fundido

resposta agradável, eu estou copiando este para ilustration de algum padrão de design tomadas a partir do código Java
madtyn

Eu estou com Python3 e este código me dáTypeError: iter() returned non-iterator
madtyn

1
@JonathanCast não tenho certeza se eu sigo. Em Python, você normalmente usaria mape anynão filter, mas poderia usar SENTINEL = object(); next(filter(predicate, arr), SENTINEL) is not SENTINELou esquecer um SENTINELe apenas usar try: excepte capturar o arquivo StopIteration.
Juanpa.arrivillaga 18/09/19

13

Além de todas as menções de StopIteration, o loop "for" do Python simplesmente faz o que você deseja:

>>> it = iter("hello")
>>> for i in it:
...     print i
...
h
e
l
l
o

7

Experimente o método __length_hint __ () de qualquer objeto iterador:

iter(...).__length_hint__() > 0

5
Eu sempre me perguntei por que diabos python tem todos esses métodos __ xxx __? Eles parecem tão feios.
mP.

6
Pergunta legítima! Geralmente é a sintaxe dos métodos expostos por uma função interna (por exemplo, len, na verdade, está chamando len ). Essa função interna não existe para length_hint, mas na verdade é uma proposta pendente (PEP424).
Fulmicoton

1
@mP. essas funções estão lá, porque às vezes são necessárias. Eles são intencionalmente feios, porque são considerados o método de último recurso: se você os usar, sabe que faz algo não-pitônico e potencialmente perigoso (o que também pode parar de funcionar a qualquer momento).
Arne Babenhauserheide

Gosta __init__e __main__? Imho, é um pouco confuso, não importa o que você tente justificar.
user1363990


4

Você pode teeusar o iterador,, itertools.teee verificar StopIterationo iterador inicial.


3

Não. O conceito mais semelhante é provavelmente uma exceção StopIteration.


10
O Python usa exceções para o fluxo de controle? Parece muito ingênuo.
mP.

5
Certo: exceções devem ser usadas para manipular erros, não para definir o fluxo normal de controle.
Giorgio


1

O caso de uso que me levou a procurar isso é o seguinte

def setfrom(self,f):
    """Set from iterable f"""
    fi = iter(f)
    for i in range(self.n):
        try:
            x = next(fi)
        except StopIteration:
            fi = iter(f)
            x = next(fi)
        self.a[i] = x 

onde hasnext () está disponível, pode-se fazer

def setfrom(self,f):
    """Set from iterable f"""
    fi = iter(f)
    for i in range(self.n):
        if not hasnext(fi):
            fi = iter(f) # restart
        self.a[i] = next(fi)

o que para mim é mais limpo. Obviamente, você pode solucionar problemas definindo classes de utilitário, mas o que acontece é que você tem uma proliferação de vinte e diferentes soluções alternativas quase equivalentes, cada uma com suas peculiaridades, e se você deseja reutilizar o código que usa soluções alternativas diferentes, é necessário tenha vários equivalentes quase em seu único aplicativo ou escolha e reescreva o código para usar a mesma abordagem. A máxima "faça uma vez e faça bem" falha muito.

Além disso, o próprio iterador precisa ter uma verificação interna 'hasnext' para executar para ver se precisa gerar uma exceção. Essa verificação interna é ocultada para que precise ser testada tentando obter um item, capturando a exceção e executando o manipulador, se acionado. Isso é desnecessário ocultar o IMO.


1
Para este caso de uso, você pode usar itertools.cycle
eaglebrain

0

A maneira sugerida é StopIteration . Por favor, veja o exemplo de Fibonacci do tutorialspoint

#!usr/bin/python3

import sys
def fibonacci(n): #generator function
   a, b, counter = 0, 1, 0
   while True:
      if (counter > n): 
         return
      yield a
      a, b = b, a + b
      counter += 1
f = fibonacci(5) #f is iterator object

while True:
   try:
      print (next(f), end=" ")
   except StopIteration:
      sys.exit()

-2

A maneira como resolvi meu problema é manter a contagem do número de objetos repetidos até o momento. Eu queria iterar sobre um conjunto usando chamadas para um método de instância. Como eu sabia a duração do set e o número de itens contados até agora, eu efetivamente tive umahasNext método.

Uma versão simples do meu código:

class Iterator:
    # s is a string, say
    def __init__(self, s):
        self.s = set(list(s))
        self.done = False
        self.iter = iter(s)
        self.charCount = 0

    def next(self):
        if self.done:
            return None
        self.char = next(self.iter)
        self.charCount += 1
        self.done = (self.charCount < len(self.s))
        return self.char

    def hasMore(self):
        return not self.done

Obviamente, o exemplo é de brinquedo, mas você entendeu. Isso não funcionará nos casos em que não há como obter o comprimento do iterável, como um gerador etc.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.