在Python迭代器中具有hasNext?


Answers:


106

不,没有这样的方法。迭代结束由异常指示。请参阅文档


71
“请求宽恕比允许容易。”

118
“请求宽容比允许要容易。”:检查迭代器是否具有下一个元素并不要求允许。在某些情况下,您想测试下一个元素的存在而不消耗它。如果有一种unnext()方法可以通过调用来检查第一个元素是否存在,那么我将接受try catch解决方案next()
乔治

15
@Giorgio,如果不执行另一个生成元素的代码,就无法知道另一个元素是否存在(您不知道生成器是否将执行yield)。当然,编写存储next()并提供has_next()和的结果的适配器并不难move_next()
avakar 2012年

5
可以使用相同的想法来实现该hasNext()方法(成功时生成,缓存并返回true,失败时返回false)。然后这两个hasNext()next()将取决于一个普遍的根本getNext()方法和缓存项。我真的不明白next(),如果实现提供它的适配器非常容易,为什么不应该将它放在标准库中。
乔治

3
@LarsH:您的意思是例如从文件中读取的迭代器可以在读取文件时进行更改?我同意这可能是一个问题(这会影响任何提供的库next()hasNext()方法,而不仅仅是一个假设的Python库)。所以,是的,next()而且hasNext()变得非常棘手,如果被扫描的数据流的内容取决于读取元素。
Giorgio 2013年

238

StopIteration使用可以替代next(iterator, default_value)

例如:

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

因此None,如果您不想使用异常方法,则可以为迭代器的末尾检测或其他预先指定的值。


69
如果将“无”用作“前哨”,则最好确保迭代器没有任何“无”。您也可以使用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

这比例外还漂亮
daddinhquoc

问题在于,这样,您也从迭代器中消费了下一个值。Java中的hasNext不会消耗下一个值。
艾伦·弗朗佐尼

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检查数组是否包含与给定谓词匹配的元素。Python社区的傲慢和短视令人震惊。
乔纳森·

好的答案,我正在复制此内容,以说明从Java代码中提取的某些设计模式
madtyn

我使用Python3,并且这段代码给了我TypeError: iter() returned non-iterator
madtyn

1
@JonathanCast不确定我是否遵循。在Python中,通常会使用mapany来代替filter,但是您可能会使用SENTINEL = object(); next(filter(predicate, arr), SENTINEL) is not SENTINEL或忘记一个,SENTINEL而只是使用try: except并捕获StopIteration
juanpa.arrivillaga 18/09/18

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 __方法?他们看起来很丑。
mP。

6
正当问题!通常,它是内置函数公开的方法的语法(例如len,实际上是在调用len)。对于length_hint不存在这样的内置函数,但实际上它是一个挂起的建议(PEP424)。
fulmicoton

1
@mP。这些功能在那里,因为有时需要它们。它们故意是丑陋的,因为它们被认为是不得已的方法:如果使用它们,您知道您做的事情是非Python的,并且有潜在的危险(也可能随时停止工作)。
Arne Babenhauserheide

喜欢__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和进行迭代,并检查StopIterationteed迭代器。




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”检查,以查看是否需要引发异常。然后隐藏此内部检查,以便需要通过尝试获取项目,捕获异常并在抛出异常时运行处理程序来对其进行测试。这是不必要的隐藏IMO。


1
对于此用例,您可以使用itertools.cycle
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

当然,示例是一个玩具,但是您知道了。在无法获得迭代器长度的情况下(例如生成器等),这将不起作用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.