Python迭代器有has_next方法吗?


当前回答

不,没有这样的方法。迭代的结束是由StopIteration表示的(更多信息在这里)。


这遵循了python原则EAFP(请求原谅比请求许可更容易)。has_next方法将遵循LBYL原则(三思而后行),并与python的核心原则相矛盾。

这篇有趣的文章更详细地解释了这两个概念。

其他回答

非常有趣的问题,但是这个“hasnext”的设计已经放进了leetcode: https://leetcode.com/problems/iterator-for-combination/

这是我的实现:

class CombinationIterator:

def __init__(self, characters: str, combinationLength: int):
    from itertools import combinations
    from collections import deque
    self.iter = combinations(characters, combinationLength)
    self.res = deque()


def next(self) -> str:
    if len(self.res) == 0:
        return ''.join(next(self.iter))
    else:
        return ''.join(self.res.pop())


def hasNext(self) -> bool:
    try:
        self.res.insert(0, next(self.iter))
        return True
    except:
        return len(self.res) > 0

我认为当你想要某种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值的迭代器。

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

不。最类似的概念很可能是StopIteration异常。

也许只有我这么想,但虽然我喜欢https://stackoverflow.com/users/95810/alex-martelli的答案,但我发现这个更容易读:

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

class MyIterator(Iterator):  # need to subclass Iterator rather than object
  def __init__(self, it):
    self._iter = iter(it)
    self._sentinel = object()
    self._next = next(self._iter, self._sentinel)
    
  def __iter__(self): 
    return self
  
  def __next__(self):        # __next__ vs next in python 2
    if not self.has_next():
      next(self._iter)  # raises StopIteration

    val = self._next
    self._next = next(self._iter, self._sentinel)
    return val
  
  def has_next(self):
    return self._next is not self._sentinel

也可以实现一个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()是完全足够的,没有造成任何问题,事实上是非常有用的。你只需要知道它的预读逻辑。