11

我有一个像这样的发电机:

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 并抛出异常。

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

4

5 回答 5

8

只需做一个简单的检查:

def iterate_my_objects_if_something(self):
    if self.my_objects:
        for x in self.my_objects:
            if x.something:
                yield x
于 2013-07-18T01:47:02.840 回答
4

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

生成器上的迭代是一个问题吗?

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

  • 空生成器不是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)
    

生成器内部的迭代是否有问题?

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

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
    
于 2013-07-18T02:03:21.353 回答
3

迭代前检查:

if self.my_objects:
    for x in self.my_objects:
        if x.something:
          yield x
于 2013-07-18T01:45:00.723 回答
0

如果没有什么可以迭代抛出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)
于 2020-04-22T19:46:11.120 回答
0

这个答案在另一个问题中得到了回答:raise StopIteration 和生成器中的 return 语句有什么区别?.

基本上,您只需要返回 None 即可结束您的生成器。提高StopIteration 现在已弃用

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

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

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

于 2020-11-10T09:06:12.457 回答