【问题标题】:How to make an iterator that filters lists?如何制作过滤列表的迭代器?
【发布时间】:2018-08-16 15:31:41
【问题描述】:

我有一个过滤列表的生成器函数:

def gen_func(numbers):
    for number in numbers:
        if "?" in number:
            yield number

for item in gen_func(["trunk", "tr?ee", "+lea?f"]):
    print(item)

输出:

tr?ee
lea?f

我尝试编写一个期望得到相同输出的迭代器,但我得到一个 None 作为第一项返回:

class GenClass:

    def __init__(self, numbers):
        self.current = 0
        self.numbers = numbers

    def __iter__(self):
        return self

    def __next__(self):
        next_value = self.current   

        if next_value >= len(self.numbers):
            raise StopIteration
        output = self.numbers[self.current]
        self.current += 1

        if "?" in output:
            return output

genclass = GenClass(["trunk", "tr?ee", "+lea?f"])
for num in genclass:
    print(num)

输出:

None
tr?ee
lea?f

我怎样才能得到tr?eelea?f 而不是上面的? 另外,这个迭代器有意义还是我在这里滥用了迭代器?

【问题讨论】:

  • if "?" in output: 如果没有......你返回None
  • 我的猜测是 next 仍然返回,您可能需要调用 self.__iter__ 才能跳转到下一个位置。类似的东西。
  • @juanpa.arrivillaga 我明白了。如何不返回无? continue 在这里不起作用。
  • while True: if something: return foo

标签: python iterator generator


【解决方案1】:

您需要将__next__的代码改为:

        next_value = self.current

        if next_value >= len(self.numbers):
            raise StopIteration

        output = self.numbers[self.current]
        while "?" not in output:
            self.current += 1
            output = self.numbers[self.current]

        self.current += 1
        return output

这会返回:

tr?ee
+lea?f

本质上,您循环遍历数字并仅在数字包含 ? 时返回。

【讨论】:

    【解决方案2】:

    您可以使用 yield 代替 return 并且只执行 __iter__。

    class GenClass:
        def __init__(self, numbers):
            self.numbers = numbers
    
        def __iter__(self):
            for value  in self.numbers:
                if '?' in value:
                    yield value
    
    
    genclass = GenClass(["trunk", "tr?ee", "+lea?f"])
    for num in genclass:
        print(num)
    
    Out:
    tr?ee
    +lea?f
    

    我上面的解决方案不是 iterator 而是 iterable,谢谢juanpa.arrivillaga。我认为最好将 iterableiterator 分开,在这种情况下,您可以分别实现 GenClass 和 iterator。因此,如果您将遍历一次 GenClass 实例,则可以重复它而无需重置计数器(来自Daniel Mesejo 的解决方案的 GenClassIterator)。

    class GenClass:
        def __init__(self, numbers):
            self.numbers = numbers
    
        def __iter__(self):
            return GenClassIterator(self.numbers)
    
    
    class GenClassIterator:
        def __init__(self, numbers):
            self.numbers = numbers
            self.current = 0
    
        def __next__(self):
            next_value = self.current
    
            if next_value >= len(self.numbers):
                raise StopIteration
    
            output = self.numbers[self.current]
            while "?" not in output:
                self.current += 1
                output = self.numbers[self.current]
    
            self.current += 1
            return output
    
        def __iter__(self):
            return self
    

    【讨论】:

    • 可以这样做,但是你定义了一个iterable而不是一个迭代器。那可能没问题。但是 OP 已经有了这个解决方案。
    • 你是对的。因此,如果需要 iterator,我认为,最好将 iterable 对象和 iterator 本身分开。在这种情况下,iterable 可以在不重置计数器的情况下重复使用
    【解决方案3】:

    不断从numbers弹出项目,直到有一个符合条件然后返回。

    import collections
    class GenClass:
    
        def __init__(self, numbers):
            self.numbers = collections.deque(numbers)
    
        def __iter__(self):
            return self
    
        def __next__(self):
            while True:
                try:
                    next_value = self.numbers.popleft()
                except IndexError as e:
                    raise StopIteration
    
                if "?" in next_value:
                    return next_value
    

    【讨论】:

      【解决方案4】:

      您可以调用 next(self) 来返回所需的值而不返回 None

      class GenClass:
      
      def __init__(self, numbers):
          self.current = 0
          self.numbers = numbers
      
      def __iter__(self):
          return self
      
      def __next__(self):
          next_value = self.current   
      
          if next_value >= len(self.numbers):
              raise StopIteration
          output = self.numbers[self.current]
          self.current += 1
      
          if "?" in output:
              return output
          else:
              return next(self)  # Skip value
      
      
      genclass = GenClass(["trunk", "tr?ee", "+lea?f"])
      for num in genclass:
           print(num)
      

      这是一个更新版本,在迭代期间不存储所有值

      class GenClass:
      
      def __init__(self, numbers):
          self.numbers = iter(numbers)
      
      def __iter__(self):
          return self
      
      def __next__(self):
          output = next(self.numbers)
          if "?" in output:
              return output
          else:
              return next(self)
      

      【讨论】:

        猜你喜欢
        • 2020-12-23
        • 1970-01-01
        • 2011-08-05
        • 2015-07-27
        • 1970-01-01
        • 2021-04-14
        • 2016-04-12
        • 2017-05-30
        • 2019-06-13
        相关资源
        最近更新 更多