2009-12-27 2 views
91

Неужели Итераторы Python получили метод hasNext?hasNext в итераторах Python?

+0

Связанный: [Как узнать, сгенерирован ли генератор с самого начала?] (Http: // stackoverflow.com/questions/661603/how-do-i-know-if-a-generator-is-empty-from-the-start) – user2314737

ответ

64

Нет, нет такого метода. Исключение составляет конец итерации. См. documentation.

+50

«Легче попросить прощения, чем разрешения». –

+66

«Легче попросить прощения, чем разрешение».: Проверка того, имеет ли итератор следующий элемент, не запрашивает разрешения. Есть ситуации, когда вы хотите проверить существование следующего элемента, не потребляя его. Я бы принял решение catch catch, если был метод 'unnext()', чтобы вернуть первый элемент после проверки того, что он существует, вызывая 'next()'. – Giorgio

+9

@Giorgio, нет способа узнать, существует ли другой элемент без выполнения кода, который его генерирует (вы не знаете, будет ли генератор выполнять 'yield' или нет). Конечно, нетрудно написать адаптер, который хранит результат 'next()' и предоставляет 'has_next()' и 'move_next()'. – avakar

3

No. Наиболее близкое понятие, скорее всего, StopIteration exception.

+7

Что Python использует исключения для потока управления? Звучит довольно нафтодно. –

+4

Вправо: исключения должны использоваться для обработки ошибок, а не для определения нормального потока управления. – Giorgio

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 
28

Если вы действительно необходимо a 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 особенности, таким образом, я пытаюсь предупредить подобные комментарии на этот раз ;-)]]

+1

«Верно переписывая алгоритм из эталонной реализации в Java» является худшей причиной для использования метода 'has_next'. Дизайн Python делает невозможным, скажем, использовать 'filter', чтобы проверить, содержит ли массив элемент, соответствующий данному предикату. Высокомерие и близорукость сообщества Python ошеломляют. –

+0

хороший ответ, я копирую это для иллюстрации какого-то шаблона проектирования, взятого из кода Java – madtyn

+0

Я с Python3, и этот код дает мне 'TypeError: iter() возвращен не-итератор' – madtyn

8

в дополнение ко всем упоминает о StopIteration, Питон «для» петли просто делает то, что вы хотите:

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

Вы можете tee итератор, используя, itertools.tee и проверьте StopIteration на первый удар итератора.

8

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

iter(...).__length_hint__() > 0 
+4

Я всегда задавался вопросом, почему на земле у python есть все эти методы __ xxx __? Они кажутся такими уродливыми. –

+6

Законный вопрос! Обычно это синтаксис для методов, которые выставляются встроенной функцией (например, len, на самом деле вызывает __len__). Такая встроенная функция не существует для length_hint, но это фактически ожидающее предложение (PEP424). – fulmicoton

+1

@ mP. эти функции существуют, потому что они иногда необходимы. Они преднамеренно уродливы, потому что они считаются последним средством: если вы их используете, вы знаете, что делаете что-то непитоновое и потенциально опасное (что также может перестать работать в любой момент). –

120

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

Для exapmle:

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

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

+43

, если вы используете None в качестве «дозорного», вы должны быть уверены, что ваш итератор не имеет никаких Nones. вы также можете сделать 'sentinel = object()' и 'next (iterator, sentinel)' и test with 'is'. –

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», чтобы проверить, нужно ли ему создавать исключение. Эта внутренняя проверка затем скрыта, чтобы ее нужно было протестировать, пытаясь получить элемент, поймав исключение и запустив обработчик, если он был брошен. Это не нужно скрывать ИМО.

0

хороший подход для такого вопроса/проблемы, это проверить, что мы имеем в директории (объект/метод/итератора/тип/класс/...)

вы увидите, что dir(iterator) возвращение __length_hint__

iterator.__length_hint__() и является положительным до конца итерации.

все.

+0

'__length_hint__' не гарантируется точность: https://www.python.org/dev/peps/pep-0424/. –

0

Мне это нравится:

While(True): 
    try: 
     # Do something with the next value 
     iterator.next() 
    except StopIteration: 
     break 
+6

Почему бы просто не использовать 'for ... in'? – bfontaine

0

Так я решил свою проблему, чтобы сохранить счетчик количества объектов итерации, до сих пор. Я хотел выполнить итерацию по набору, используя вызовы метода экземпляра. Поскольку я знал длину набора и количество элементов, подсчитанных до сих пор, у меня фактически был метод 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 

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

Смежные вопросы