【问题标题】:"yield from iterable" vs "return iter(iterable)"“从可迭代产生”与“返回迭代(可迭代)”
【发布时间】:2015-07-23 03:12:38
【问题描述】:

包装(内部)迭代器时,通常必须将__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 from 版本。 return iter() 变体是我迄今为止使用的模式。

【问题讨论】:

    标签: python python-3.x iterable yield-from


    【解决方案1】:

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

    其他区别:

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

    • 使用yield from,您可以在前后插入其他yield 表达式(尽管您同样可以使用itertools.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
    

    【讨论】:

    • 假设目标是简单地转移/暴露底层迭代,即不修改/注入其他项目,我想我会坚持iter(),因为:1)如果我不添加任何额外的行为,我不需要它出现在异常回溯中(我认为?)。 2)为了避免使用全局iter(),我可以调用return internal_iterable.__iter__()。关于你提出的第三点,我得想想这意味着什么。
    猜你喜欢
    • 1970-01-01
    • 2021-08-24
    • 1970-01-01
    • 2016-12-25
    • 1970-01-01
    • 2012-05-21
    • 1970-01-01
    • 2020-11-01
    • 2019-06-03
    相关资源
    最近更新 更多