CodeQL 文档

__iter__ 方法返回非迭代器

ID: py/iter-returns-non-iterator
Kind: problem
Security severity: 
Severity: error
Precision: high
Tags:
   - reliability
   - correctness
Query suites:
   - python-security-and-quality.qls

点击查看 CodeQL 代码库中的查询

类的 __iter__ 方法应始终返回迭代器。

对于 Python 3,迭代器必须同时实现 __next____iter__,对于 Python 2,迭代器必须同时实现 next__iter__。迭代器的 __iter__ 方法必须返回迭代器对象本身。

Python 中的迭代依赖于这种行为,尝试迭代具有不正确的 __iter__ 方法的类的实例可能会引发 TypeError

建议

确保 __iter__ 返回的值实现了完整的迭代器协议。

示例

在这个示例中,我们实现了我们自己的 range 版本,扩展了正常的功能,可以通过使用 skip 方法来跳过一些元素。但是,迭代器 MyRangeIterator 并没有完全实现迭代器协议(即它缺少 __iter__)。

在表面上,对范围中的元素进行迭代似乎有效,例如代码 x = sum(my_range) 给出了预期的结果。但是,如果我们运行 sum(iter(my_range)),我们会得到一个 TypeError: 'MyRangeIterator' object is not iterable

如果我们尝试使用自定义方法跳过一些元素,例如 y = sum(my_range.skip({6,9})),这也会引发 TypeError

解决方法是在 MyRangeIterator 中实现 __iter__ 方法。

class MyRange(object):
    def __init__(self, low, high):
        self.low = low
        self.high = high

    def __iter__(self):
        return MyRangeIterator(self.low, self.high)

    def skip(self, to_skip):
        return MyRangeIterator(self.low, self.high, to_skip)

class MyRangeIterator(object):
    def __init__(self, low, high, skip=None):
        self.current = low
        self.high = high
        self.skip = skip

    def __next__(self):
        if self.current >= self.high:
            raise StopIteration
        to_return = self.current
        self.current += 1
        if self.skip and to_return in self.skip:
            return self.__next__()
        return to_return

    # Problem is fixed by uncommenting these lines
    # def __iter__(self):
    #     return self

my_range = MyRange(0,10)
x = sum(my_range) # x = 45
y = sum(my_range.skip({6,9})) # TypeError: 'MyRangeIterator' object is not iterable

参考

  • ©GitHub, Inc.
  • 条款
  • 隐私