hasNext в итераторах Python?

Ответы:

107

Нет, такого метода нет. Конец итерации обозначен исключением. Смотрите документацию .

avakar
источник
71
«Проще просить прощения, чем разрешения».
119
«Проще просить прощения, чем разрешения». Проверка наличия следующего элемента у итератора не требует разрешения. Существуют ситуации, в которых вы хотите проверить наличие следующего элемента, не потребляя его. Я бы принял решение try catch, если бы был unnext()метод, чтобы вернуть первый элемент после того, как я проверил его существование, вызвав его next().
Джорджио
15
@ Джорджио, нет способа узнать, существует ли другой элемент без выполнения кода, который его генерирует (вы не знаете, будет ли работать генератор yieldили нет). Конечно, нетрудно написать адаптер, который хранит результат, next()а также предоставляет has_next()и move_next().
Авакар
5
Эта же идея может быть использована для реализации hasNext()метода (для создания, кэширования и возврата true при успехе или возврата false при сбое). Тогда и то hasNext()и другое next()будет зависеть от общего базового getNext()метода и кэшируемого элемента. Я действительно не понимаю, почему next()не должно быть в стандартной библиотеке, если это так легко реализовать адаптер, который обеспечивает его.
Джорджио
3
@LarsH: Вы имеете в виду, например, итератор, который читает из файла, который можно изменить во время чтения из него? Я согласен, что это может быть проблемой (которая затрагивает любую библиотеку, предоставляющую метод next()и hasNext()метод, а не только гипотетическую библиотеку Python). Так что да, next()и hasNext()становится сложно, если содержимое сканируемого потока зависит от того, когда элементы читаются.
Джорджио
239

Есть альтернатива StopIterationс помощью next(iterator, default_value).

Например:

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

Таким образом, вы можете обнаружить Noneили другое заранее заданное значение для конца итератора, если вы не хотите использовать метод исключения.

Деррик Чжан
источник
70
если вы используете None в качестве «стража», лучше всего быть уверенным, что у вашего итератора нет None. Вы также можете сделать sentinel = object()и next(iterator, sentinel)и проверить с is.
Сам Boosalis
1
После @samboosalis я бы предпочел использовать встроенный unittest.mock.sentinelобъект, который позволяет вам написать явное, next(a, sentinel.END_OF_ITERATION)а затемif next(...) == sentinel.END_OF_ITERATION
ClementWalter
это красивее, чем исключение
datdinhquoc
Проблема в том, что таким образом вы также ПОТРЕБЛЯЕТЕ следующее значение от итератора. hasNext в Java не использует следующее значение.
Алан Францони
40

Если вам действительно нужно в has-nextфункциональности (потому что вы просто верно расшифровывать алгоритм от эталонной реализации в Java, скажем, или потому , что вы пишете прототип , который будет нужно быть легко расшифрованы в Java , когда она будет закончена), это легко получить это с небольшим классом обертки. Например:

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

теперь что-то вроде

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

излучает

c
i
a
o

как требуется.

Обратите внимание, что использование next(sel.it)в качестве встроенного требует Python 2.6 или выше; если вы используете более старую версию Python, используйте self.it.next()вместо этого (и аналогично для next(x)примера использования). [[Вы можете разумно думать, что эта заметка является излишней, поскольку Python 2.6 существует уже более года - но чаще, когда я использую функции Python 2.6 в ответе, некоторые комментаторы или другие считают необходимым указать на это что это 2,6 функции, поэтому я пытаюсь предупредить такие комментарии на этот раз ;-)]]

Алекс Мартелли
источник
9
«добросовестная расшифровка алгоритма из эталонной реализации в Java» - худшая причина, по которой нужен has_nextметод. Дизайн Python делает невозможным, скажем, использование filterдля проверки, содержит ли массив элемент, соответствующий данному предикату. Высокомерие и близорукость сообщества Python ошеломляют.
Джонатан Каст
хороший ответ, я копирую это для иллюстрации некоторого шаблона проектирования, взятого из кода Java
madtyn
Я с Python3, и этот код дает мнеTypeError: iter() returned non-iterator
madtyn
1
@JonathanCast не уверен, что я следую. В Python вы обычно используете mapи anyвместо filter, но вы можете использовать SENTINEL = object(); next(filter(predicate, arr), SENTINEL) is not SENTINELили забыть SENTINELи просто использовать try: exceptи поймать StopIteration.
juanpa.arrivillaga
13

В дополнение ко всем упоминаниям StopIteration цикл Python for просто делает то, что вы хотите:

>>> it = iter("hello")
>>> for i in it:
...     print i
...
h
e
l
l
o
Брайан Клаппер
источник
7

Попробуйте метод __length_hint __ () из любого объекта итератора:

iter(...).__length_hint__() > 0
юй
источник
5
Я всегда задавался вопросом, почему в Python есть все эти методы __ xxx __? Они кажутся такими уродливыми.
мП
6
Законный вопрос! Обычно это синтаксис методов, предоставляемых встроенной функцией (например, len, фактически вызывает len ). Такая встроенная функция не существует для length_hint, но на самом деле это ожидающее предложение (PEP424).
fulmicoton
1
@mP. эти функции есть, потому что они иногда необходимы. Они намеренно безобразны, потому что они рассматриваются как метод последней инстанции: если вы используете их, вы знаете, что делаете что-то непитонное и потенциально опасное (что также может перестать работать в любой момент).
Арне Бабенхаузерхайде
Вроде __init__и __main__? Имхо, это немного беспорядок, независимо от того, пытаетесь ли вы оправдать это.
user1363990
5

hasNextнесколько переводит на StopIterationисключение, например:

>>> it = iter("hello")
>>> it.next()
'h'
>>> it.next()
'e'
>>> it.next()
'l'
>>> it.next()
'l'
>>> it.next()
'o'
>>> it.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
Мик
источник
4

Вы можете teeиспользовать итератор itertools.teeи проверить его StopIterationна итераторе teed.

Сикора
источник
3

Нет. Наиболее похожая концепция, скорее всего, является исключением StopIteration.

Джеймс Томпсон
источник
10
Какой Python использует исключения для потока управления? Звучит довольно наивно.
мП
5
Справа: исключения должны использоваться для обработки ошибок, а не для определения нормального потока управления.
Джорджио
1

Вариант использования, который привел меня к поиску этого заключается в следующем

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 

где hasnext () доступен, можно сделать

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)

который для меня чище. Очевидно, что вы можете обойти проблемы, определив служебные классы, но в результате вы получаете двадцать с лишним различных почти эквивалентных обходных путей, каждый из которых имеет свои причуды, и если вы хотите повторно использовать код, использующий разные обходные пути, вы должны либо иметь несколько почти эквивалентных в вашем приложении, или просто перебирать и переписывать код, чтобы использовать тот же подход. Принцип «сделай это один раз и сделай это хорошо» терпит неудачу.

Кроме того, сам итератор должен иметь внутреннюю проверку hasnext, чтобы увидеть, нужно ли ему вызывать исключение. Эта внутренняя проверка затем скрывается, поэтому ее необходимо протестировать, пытаясь получить элемент, перехватить исключение и запустить обработчик, если его сгенерировать. Это ненужное сокрытие ИМО.

Джон Аллсуп
источник
1
Для этого варианта
eaglebrain
0

Предложенный способ - StopIteration . Пожалуйста, посмотрите пример Фибоначчи с 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

Способ, которым я решил свою проблему, заключается в том, чтобы до сих пор вести подсчет количества объектов, повторяемых. Я хотел перебрать множество, используя вызовы метода экземпляра. Так как я знал длину сета и количество посчитанных предметов, у меня был эффективный hasNextметод.

Простая версия моего кода:

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

Конечно, пример игрушечный, но вы поняли идею. Это не будет работать в тех случаях, когда нет способа получить длину итерируемого, например, генератора и т. Д.

forumulator
источник