24

包装(内部)迭代器时,通常必须将__iter__方法重新路由到底层迭代器。考虑以下示例:

class FancyNewClass(collections.Iterable):
    def __init__(self):
        self._internal_iterable = [1,2,3,4,5]

    # ...

    # variant A
    def __iter__(self):
        return iter(self._internal_iterable)

    # variant B
    def __iter__(self):
        yield from self._internal_iterable

变体 A 和 B 之间是否存在显着差异?变体 A 返回一个迭代器对象,该对象已通过iter()内部可迭代对象进行查询。变体 B 返回一个生成器对象,该对象从内部可迭代对象中返回值。出于某种原因,其中一个更可取吗?在版本中使用collections.abc。变体是我到目前为止使用的模式yield fromreturn iter()

4

1 回答 1

18

唯一显着的区别是当从可迭代对象中引发异常时会发生什么。使用return iter()你的FancyNewClass不会出现在异常回溯中,而使用yield from它会。拥有尽可能多的回溯信息通常是一件好事,尽管在某些情况下您可能想要隐藏您的包装器。

其他区别:

  • return iter必须从全局变量中加载名称iter- 这可能很慢(尽管不太可能显着影响性能)并且可能会被弄乱(尽管任何像这样覆盖全局变量的人都应该得到他们所得到的)。

  • 您可以在前后yield from插入其他表达式(尽管您可以同样使用)。yielditertools.chain

  • 如前所述,yield from表单会丢弃任何生成器返回值(即raise StopException(value)。您可以改写return (yield from iterator).

这是一个比较两种方法的反汇编并显示异常回溯的测试:http: //ideone.com/1YVcSe

使用return iter()

  3           0 LOAD_GLOBAL              0 (iter)
              3 LOAD_FAST                0 (it)
              6 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
              9 RETURN_VALUE
Traceback (most recent call last):
  File "./prog.py", line 12, in test
  File "./prog.py", line 10, in i
RuntimeError

使用return (yield from)

  5           0 LOAD_FAST                0 (it)
              3 GET_ITER
              4 LOAD_CONST               0 (None)
              7 YIELD_FROM
              8 RETURN_VALUE
Traceback (most recent call last):
  File "./prog.py", line 12, in test
  File "./prog.py", line 5, in bar
  File "./prog.py", line 10, in i
RuntimeError
于 2015-05-12T10:32:29.303 回答