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


Відповіді:


106

Ні, такого методу немає. Кінець ітерації позначається винятком. Дивіться документацію .


71
"Простіше просити пробачення, ніж дозволу".

118
"Простіше просити пробачення, ніж дозволу.": Перевірка наявності в ітераторі наступного елемента не вимагає дозволу. Є ситуації, в яких потрібно перевірити наявність наступного елемента, не використовуючи його. Я б прийняв рішення try catch, якби був unnext()метод повернути перший елемент назад після того, як я перевірив його наявність, зателефонувавши next().
Джорджіо

15
@Giorgio, немає ніякого способу дізнатися, чи існує інший елемент без виконання коду, який його генерує (ти не знаєш, чи виконає генератор yieldчи ні). Звичайно, не важко написати адаптер, який зберігає результат next()і надає has_next()і move_next().
авакар

5
Ця ж ідея може бути використана для реалізації hasNext()методу (виробляти, кешувати і повертати істину на успіх, або повертати помилкову помилку). Тоді і те, 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" як "дозорний", ви впевнені, що в його ітераторі немає жодних "Nones". ви можете також зробити sentinel = object()і next(iterator, sentinel)і тест з is.
sam boosalis

1
після @samboosalis я скоріше використовую вбудований unittest.mock.sentinelоб’єкт, який дозволяє написати явний, next(a, sentinel.END_OF_ITERATION)а потімif next(...) == sentinel.END_OF_ITERATION
ClementWalter

це гарніше, ніж виняток
datdinhquoc

Проблема полягає в тому, що, таким чином, ви СПОЖИВАєте наступне значення з ітератора. hasNext на Java не використовує наступне значення.
Алан Францоні

39

Якщо вам дійсно потрібно в 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для перевірки, чи містить масив елемент, що відповідає заданому предикату. Зарозумілість та недалекоглядність спільноти Пітон є приголомшливою.
Джонатан У ролях

приємна відповідь, я
копіюю

Я з 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).
фулмікотон

1
@mP. ці функції є, тому що вони іноді потрібні. Вони навмисно некрасиві, тому що їх вважають як крайній спосіб: якщо ви їх використовуєте, ви знаєте, що робите щось непітонічне та потенційно небезпечне (що також може припинити роботу в будь-який момент).
Arne Babenhauserheide

Як __init__і __main__? Імхо, це трохи безлад, незалежно від того, намагаєтеся ви виправдати це.
користувач1363990

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ітератора підлітка.


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
Для цього випадку ви можете скористатися itertools.cycle
орлан

0

Запропонований спосіб - StopIteration . Будь ласка, дивіться приклад Фібоначчі з підручника

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

Звичайно, приклад - іграшковий, але ви отримуєте ідею. Це не спрацює у випадках, коли немає можливості отримати довжину ітерабельного, наприклад генератора тощо.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.