Python迭代器有has_next方法吗?


当前回答

我认为当你想要某种has_next功能时,有一些有效的用例,在这种情况下,你应该用定义了has_next的迭代器来装饰迭代器。

结合这个问题的答案的概念,这里是我的实现,这对我来说是一个很好的简洁的解决方案(python 3.9):

_EMPTY_BUF = object()


class BufferedIterator(Iterator[_T]):
    def __init__(self, real_it: Iterator[_T]):
        self._real_it = real_it
        self._buf = next(self._real_it, _EMPTY_BUF)

    def has_next(self):
        return self._buf is not _EMPTY_BUF

    def __next__(self) -> _T_co:
        v = self._buf
        self._buf = next(self._real_it, _EMPTY_BUF)
        if v is _EMPTY_BUF:
            raise StopIteration()
        return v

主要的区别是has_next只是一个布尔表达式,也处理None值的迭代器。

将此添加到这里的测试和示例使用的要点。

其他回答

也可以实现一个helper生成器,它包装任何迭代器,并回答问题,如果它有next值:

在网上试试!

def has_next(it):
    first = True
    for e in it:
        if not first:
            yield True, prev
        else:
            first = False
        prev = e
    if not first:
        yield False, prev

for has_next_, e in has_next(range(4)):
    print(has_next_, e)

输出:

True 0
True 1
True 2
False 3

该方法的主要缺点(可能也是唯一的缺点)是它会多预读一个元素,对于大多数任务来说,它是完全可以的,但对于某些任务,它可能是不允许的,特别是如果has_next()的用户没有意识到这种预读逻辑,可能会误用它。

上面的代码也适用于无限迭代器。

实际上,对于所有的情况下,我曾经编写这样的has_next()是完全足够的,没有造成任何问题,事实上是非常有用的。你只需要知道它的预读逻辑。

为了读取所有迭代,基于处理“StopIteration”执行的解决方法非常简单:

    end_cursor = False
    while not end_cursor:
        try:
            print(cursor.next())
        except StopIteration:
            print('end loop')
            end_cursor = True
        except:
            print('other exceptions to manage')
            end_cursor = True

我相信python只有next(),根据文档,如果没有更多的元素,它就会抛出异常。

http://docs.python.org/library/stdtypes.html#iterator-types

如果您确实需要一个has-next功能,那么使用一个小包装器类很容易获得它。例如:

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

是必需的。

Note that the use of next(sel.it) as a built-in requires Python 2.6 or better; if you're using an older version of Python, use self.it.next() instead (and similarly for next(x) in the example usage). [[You might reasonably think this note is redundant, since Python 2.6 has been around for over a year now -- but more often than not when I use Python 2.6 features in a response, some commenter or other feels duty-bound to point out that they are 2.6 features, thus I'm trying to forestall such comments for once;-)]]

===

对于Python3,您将进行以下更改:

from collections.abc import Iterator  # since python 3.3 Iterator is here

class hn_wrapper(Iterator):  # need to subclass Iterator rather than object
  def __init__(self, it):
    self.it = iter(it)
    self._hasnext = None
    
  def __iter__(self): 
    return self
  
  def __next__(self):        # __next__ vs next in python 2
    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

我解决问题的方法是保留到目前为止迭代的对象数量的计数。我想通过调用实例方法遍历一个集合。因为我知道集合的长度,以及到目前为止计数的项的数量,所以我有效地使用了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

当然,这个例子是一个玩具,但您可以理解。这在无法获取可迭代对象长度的情况下不起作用,比如生成器等。