【问题标题】:Python - Generator case where nothing to returnPython - 没有任何回报的生成器案例
【发布时间】:2013-07-16 18:59:47
【问题描述】:

我有一个像这样的生成器:

def iterate_my_objects_if_something(self):
    for x in self.my_objects:
        if x.something:
            yield x

我这样称呼:

for x in self.iterate_my_objects_if_something():
    pass

在没有返回的情况下,这会尝试遍历 NoneType 并抛出异常。

我如何返回一个空的生成器?

【问题讨论】:

  • 当你没有任何对象时,为什么my_objects None?为什么它不是一个没有对象的列表?
  • 我认为您可能误诊了问题。只有当self.my_objectsNone 时才会出现此错误,而不是在没有产生任何对象的情况下。
  • 你可能应该发布堆栈跟踪,我敢打赌,异常是在生成器引发的。如果您无法遍历self.my_objects,您需要做的就是跳过循环,或者在生成器中捕获异常。
  • 原来我将这个生成器链接到另一个生成器中,它在那里抛出了一个错误,因为这个生成器没有返回。堆栈跟踪令人困惑。

标签: python for-loop generator


【解决方案1】:

只需做一个简单的检查:

def iterate_my_objects_if_something(self):
    if self.my_objects:
        for x in self.my_objects:
            if x.something:
                yield x

【讨论】:

    【解决方案2】:

    重要的是要知道,哪个迭代会导致错误。这肯定在回溯中指出,但在这种情况下,回溯不是必需的(继续阅读)。

    生成器上的迭代是否存在问题?

    看过之后,很明显,但值得澄清的是:

    • 空生成器不属于NoneType,因此遍历它不会导致此类问题:

      >>> def test_generator():
          for i in []:
              yield i
      
      
      >>> list(test_generator())  # proof it is empty
      []
      >>> for x in test_generator():
          pass
      
      >>> 
      
    • 生成器在定义期间被 Python 识别(我正在简化),尝试混合生成器和简单函数(例如,通过使用条件,如下所示)将是语法错误:

      >>> def test_generator_2(sth):
          if sth:
              for i in []:
                  yield i
          else:
              return []
      
      SyntaxError: 'return' with argument inside generator (<pyshell#73>, line 6)
      

    迭代inside生成器有问题吗?

    基于上面的结论是错误不是通过迭代器迭代,而是在创建时发生了什么(生成器中的代码):

    def iterate_my_objects_if_something(self):
        for x in self.my_objects:  # <-- only iteration inside generator
            if x.something:
                yield x
    

    所以在某些情况下,self.my_objects 似乎变成了None

    解决方案

    要解决这个问题:

    • 保证self.my_objects 始终是可迭代的(例如空列表[]),或者
    • 在迭代之前检查它:

      def iterate_my_objects_if_something(self):
          # checks, if value is None, otherwise assumes iterable:
          if self.my_objects is not None:
              for x in self.my_objects:
                  if x.something:
                      yield x
      

    【讨论】:

      【解决方案3】:

      迭代前检查:

      if self.my_objects:
          for x in self.my_objects:
              if x.something:
                yield x
      

      【讨论】:

      • 不要返回[]。它是一个发电机;只是放弃结束而不产生任何东西。这甚至不会编译。
      • @user2357112 如果我这样做,调用站点会出现同样的错误:'NoneType' object is not iterable
      • @ÓscarLópez:您的代码已损坏,您不能在生成器中拥有return(空的除外)。
      • @user2357112 好的,然后修复它。
      【解决方案4】:

      如果没有什么可以迭代抛出StopIteration异常。请参阅下面的示例。

      def iterate_my_objects_if_something(self):
          if not self.my_objects:
              raise StopIteration
          for x in self.my_objects:
              if x.something:
                  yield x
      

      我会更进一步:

      def iterate_my_objects_if_something(self):
          if not self.my_objects:
              raise StopIteration
          yield from (x for x in self.my_objects if x.something)
      

      【讨论】:

      【解决方案5】:

      这个答案在另一个问题中得到了回答:What is the difference between raise StopIteration and a return statement in generators?

      基本上,您只需要返回 None 即可结束您的生成器。提高StopIteration is now deprecated.

      所以你的代码可以简单地是:

      def iterate_my_objects_if_something(self):
          if self.my_objects == []:
              return
          for x in self.my_objects:
              if x.something:
                  yield x
      

      这与这个问题的答案非常接近,但更明确。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-10-23
        • 1970-01-01
        相关资源
        最近更新 更多