【问题标题】:Making a python iterator go backwards?让python迭代器倒退?
【发布时间】:2011-02-16 03:47:47
【问题描述】:

有没有办法让 python 列表迭代器向后退?

基本上我有这个

class IterTest(object):
    def __init__(self, data):
        self.data = data
        self.__iter = None

    def all(self):
        self.__iter = iter(self.data)
        for each in self.__iter:
            mtd = getattr(self, type(each).__name__)
            mtd(each)

    def str(self, item):
        print item

        next = self.__iter.next()
        while isinstance(next, int):
            print next
            next = self.__iter.next()

    def int(self, item):
        print "Crap i skipped C"

if __name__ == '__main__':
    test = IterTest(['a', 1, 2,3,'c', 17])
    test.all()

运行此代码会产生以下输出:

a
1
2
3
Crap i skipped C

我知道为什么它给了我输出,但是有没有一种方法可以让我在 str() 方法中后退一步?

编辑

好吧,也许让这一点更清楚。我不想完全反转,基本上我想知道是否有一种简单的方法可以在 python 中执行相当于 双向 迭代器的操作?

【问题讨论】:

  • 只使用reversed 有什么问题?您明确希望能够倒退,还是只希望结果倒退?
  • 哦....你想停止中间迭代并备份吗?您可能必须缓存已遍历的列表部分并使用reversed() 遍历它
  • 我可以这样做,但是这最终会导致 icky 代码,在 C++ 中我们支持双向迭代。
  • @uberjumper:生成器用于延迟评估序列的迭代器协议的概念使得通用双向迭代器在 Python 中更难实现。最好的办法是使用自定义迭代器对集合进行索引,该迭代器跟踪索引并像 Tamás 实现的那样备份。

标签: python list iterator


【解决方案1】:

不,通常你不能让 Python 迭代器倒退。但是,如果您只想后退一次,您可以尝试这样的操作:

def str(self, item):
    print item

    prev, current = None, self.__iter.next()
    while isinstance(current, int):
        print current
        prev, current = current, self.__iter.next()

然后您可以随时访问prev 中的前一个元素。

如果你真的需要一个双向迭代器,你可以自己实现一个,但它可能会引入比上述解决方案更多的开销:

class bidirectional_iterator(object):
    def __init__(self, collection):
        self.collection = collection
        self.index = 0

    def next(self):
        try:
            result = self.collection[self.index]
            self.index += 1
        except IndexError:
            raise StopIteration
        return result

    def prev(self):
        self.index -= 1
        if self.index < 0:
            raise StopIteration
        return self.collection[self.index]

    def __iter__(self):
        return self

【讨论】:

  • 是的,我试图避免这种情况,因为这会增加相当多的烦人开销:/
  • 在上面添加了一个 bidirectional_iterator 示例,因为我已经看到您已经更新了您的问题,但这可能会比我的第一个解决方案引入更多的开销。
  • 请注意,这个类产生一个合适的迭代器。您可以在其实例上手动调用 .next() 和 .prev() ,但不能利用迭代器的功能,例如在 for 循环或列表推导中传递它。这将引发TypeError: iter() returned non-iterator of type 'bidirectional_iterator'
  • 奇怪的是,它对我有用(Python 2.7.1,Mac OS X)。 AFAIK 迭代器在 Python 中所需要的只是一个 next() 方法和一个返回自身的 __iter___
  • @etuardu:你使用的是 Python 3 吗? iirc next 已重命名为 __next__
【解决方案2】:

我是否遗漏了什么或者你不能使用technique described in the Iterator section in the Python tutorial?

>>> class reverse_iterator:
...     def __init__(self, collection):
...         self.data = collection
...         self.index = len(self.data)
...     def __iter__(self):
...         return self
...     def next(self):
...         if self.index == 0:
...             raise StopIteration
...         self.index = self.index - 1
...         return self.data[self.index]
...     
>>> for each in reverse_iterator(['a', 1, 2, 3, 'c', 17]):
...     print each
... 
17
c
3
2
1
a

我知道这不会使迭代器倒退,但我很确定一般来说没有办法做到这一点。相反,编写一个迭代器,以相反的顺序遍历离散集合。

编辑您还可以使用reversed() 函数为任何集合获取反向迭代器,这样您就不必自己编写了:

>>> it = reversed(['a', 1, 2, 3, 'c', 17])
>>> type(it)
<type 'listreverseiterator'>
>>> for each in it:
...  print each
... 
17
c
3
2
1
a

【讨论】:

  • 一个更简单且等效的解决方案是使用内置的reversed,它返回一个迭代器,该迭代器以相反的顺序遍历连接。
  • @Tamás:正如你提到的那样,我正在编写编辑。直到我发布第一个回复后,我才考虑到它。
  • 我不想反转整个迭代器,我正在寻找相当于 python 中的双向迭代器。
  • 我想知道双向迭代器的概念完全解决了什么问题,而@987654327不能解决@
  • @BryanRoss:我的猜测是,当算法遇到某个值时,它会备份 n 个位置。这可以通过在遍历序列时记录序列并在记住的序列上使用reversed() 来备份几个步骤来解决。
【解决方案3】:

根据定义,迭代器是具有next() 方法的对象——没有提到prev()。因此,您要么必须缓存结果以便重新访问它们,要么重新实现迭代器,以便它按照您希望的顺序返回结果。

【讨论】:

    【解决方案4】:

    根据你的问题,听起来你想要这样的东西:

    class buffered:
        def __init__(self,it):
            self.it = iter(it)
            self.buf = []
        def __iter__(self): return self
        def __next__(self):
            if self.buf:
                return self.buf.pop()
            return next(self.it)
        def push(self,item): self.buf.append(item)
    
    if __name__=="__main__":
        b = buffered([0,1,2,3,4,5,6,7])
        print(next(b)) # 0
        print(next(b)) # 1
        b.push(42)
        print(next(b)) # 42
        print(next(b)) # 2
    

    【讨论】:

      【解决方案5】:

      您可以将您的迭代器包装在一个迭代器助手中以使其能够向后运行。它将迭代的值存储在一个集合中,并在后退时重用它们。

      class MemoryIterator:
          def __init__(self, iterator : Iterator):
              self._iterator : Iterator = iterator
              self._array = []
              self._isComplete = False
              self._pointer = 0
      
          def __next__(self):
              if self._isComplete or self._pointer < len(self._array):
                  if self._isComplete and self._pointer >= len(self._array):
                      raise StopIteration
      
                  value = self._array[self._pointer]
                  self._pointer = self._pointer + 1
                  return value
      
              try:
                  value = next(self._iterator)
                  self._pointer = self._pointer + 1
                  self._array.append(value)
                  return value
              except StopIteration:
                  self._isComplete = True
      
          def prev(self):        
              if self._pointer - 2 < 0:
                  raise StopIteration
      
              self._pointer = self._pointer - 1
              return self._array[self._pointer - 1]
      

      用法可以和这个类似:

      my_iter = iter(my_iterable_source)
      memory_iterator = MemoryIterator(my_iter)
      try:
          if forward:
              print(next(memory_iterator))
          else:
              print(memory_iterator.prev())
      except StopIteration:
          pass
      

      【讨论】:

        【解决方案6】:

        您可以通过以下代码使迭代器向后移动。

        class EnableBackwardIterator:
            def __init__(self, iterator):
                self.iterator = iterator
                self.history = [None, ]
                self.i = 0
        
            def next(self):
                self.i += 1
                if self.i < len(self.history):
                    return self.history[self.i]
                else:
                    elem = next(self.iterator)
                    self.history.append(elem)
                    return elem
        
            def prev(self):
                self.i -= 1
                if self.i == 0:
                    raise StopIteration
                else:
                    return self.history[self.i]
        

        用法:

        >>> prev = lambda obj: obj.prev()  # A syntactic sugar.
        >>> 
        >>> a = EnableBackwardIterator(iter([1,2,3,4,5,6]))
        >>> 
        >>> next(a)
        1
        >>> next(a)
        2
        >>> a.next()  # The same as `next(a)`.
        3
        >>> prev(a)
        2
        >>> a.prev()  # The same as `prev(a)`.
        1
        >>> next(a)
        2
        >>> next(a)
        3
        >>> next(a)
        4
        >>> next(a)
        5
        >>> next(a)
        6
        >>> prev(a)
        5
        >>> prev(a)
        4
        >>> next(a)
        5
        >>> next(a)
        6
        >>> next(a)
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
        StopIteration
        

        【讨论】:

          【解决方案7】:

          我认为这将帮助您解决问题

              class TestIterator():
                  def __init__(self):`
                      self.data = ["MyData", "is", "here","done"]
                      self.index = -1
                      #self.index=len(self.data)-1
              def __iter__(self):
                  return self
          
              def next(self):
                  self.index += 1
                  if self.index >= len(self.data):
                      raise StopIteration
                  return self.data[self.index]
          
              def __reversed__(self):
                  self.index = -1
                  if self.index >= len(self.data):
                      raise StopIteration
                  return self.data[self.index]
          
          r = TestIterator()
          itr=iter(r)
          print (next(itr))
          print (reversed(itr))
          

          【讨论】:

            【解决方案8】:
            ls = [' a', 5, ' d', 7, 'bc',9, ' c', 17,  '43', 55, 'ab',22, 'ac']
            direct = -1
            l = ls[::direct]
            for el in l:
                print el
            

            其中direct 是-1 用于反向或1 用于普通。

            【讨论】:

              【解决方案9】:

              Python 你可以使用列表和索引来模拟迭代器:

              a = [1,2,3]
              
              current = 1
              
              def get_next(a):
                current = a[a.index(current)+1%len(a)]
                return current
              def get_last(a):
                current = a[a.index(current)-1]
                return current # a[-1] >>> 3 (negative safe)
              

              如果您的列表包含重复项,那么您必须单独跟踪您的索引:

              a =[1,2,3]
              index = 0
              
              def get_next(a):
                index = index+1 % len(a)
                current = a[index]
                return current
              def get_last(a):
                index = index-1 % len(a)
                current = a[index-1]
                return current # a[-1] >>> 3 (negative safe)
              

              【讨论】:

                【解决方案10】:

                以相反顺序访问列表元素的迭代器:

                class ReverseIterator:
                    def __init__(self,ls):
                        self.ls=ls
                        self.index=len(ls)-1
                
                    def __iter__(self):
                        return self
                    def __next__(self):
                        if self.index<0:
                            raise StopIteration
                        result = self.ls[self.index]
                        self.index -= 1
                        return result
                

                【讨论】:

                  【解决方案11】:

                  我来这里是为了寻找一个双向迭代器。不确定这是否是 OP 正在寻找的,但它是制作双向迭代器的一种方法——通过给它一个属性来指示接下来要进入的方向:

                  class BidirectionalCounter:
                      """An iterator that can count in two directions (up
                      and down).
                      """
                  
                      def __init__(self, start):
                          self.forward = True
                          # Code to initialize the sequence
                          self.x = start
                  
                      def __iter__(self):
                          return self
                  
                      def __next__(self):
                          if self.forward:
                              return self.next()
                          else:
                              return self.prev()
                      
                      def reverse(self):
                          self.forward = not self.forward
                          
                      def next(self):
                          """Compute and return next value in sequence.
                          """
                          # Code to go forward
                          self.x += 1
                          return self.x
                      
                      def prev(self):
                          """Compute and return previous value in sequence.
                          """
                          # Code to go backward
                          self.x -= 1
                          return self.x
                  

                  演示:

                  my_counter = BidirectionalCounter(10)
                  print(next(my_counter))
                  print(next(my_counter))
                  my_counter.reverse()
                  print(next(my_counter))
                  print(next(my_counter))
                  

                  输出:

                  11
                  12
                  11
                  10
                  

                  【讨论】:

                    【解决方案12】:

                    我从 dilshad 编辑了 Python 代码(谢谢),并使用以下基于 Python 3 的代码在列表项的来回切换或说是双向的:

                    # bidirectional class
                    class bidirectional_iterator:
                    def __init__(self):
                        self.data = ["MyData", "is", "here", "done"]
                        self.index = -1
                    
                    def __iter__(self):
                        return self
                    
                    def __next__(self):
                        self.index += 1
                        if self.index >= len(self.data):
                            raise StopIteration
                        return self.data[self.index]
                    
                    def __reversed__(self):
                        self.index -= 1
                        if self.index == -1:
                            raise StopIteration
                        return self.data[self.index]
                    

                    例子:

                    >>> r = bidirectional_iterator()
                    >>> itr=iter(r)
                    >>> print (next(itr))
                    MyData
                    >>> print (next(itr))
                    is
                    >>> print (next(itr))
                    here
                    >>> print (reversed(itr))
                    is
                    >>> print (reversed(itr))
                    MyData
                    >>> print (next(itr))
                    is
                    

                    【讨论】:

                      【解决方案13】:

                      请看 Morten Piibeleht 制作的这个函数。它为可迭代的每个元素生成一个(上一个、当前、下一个)元组。

                      https://gist.github.com/mortenpi/9604377

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2015-02-25
                        • 2012-06-21
                        • 1970-01-01
                        • 2015-01-01
                        • 2021-09-14
                        • 2013-06-20
                        • 1970-01-01
                        相关资源
                        最近更新 更多