“来自可迭代的收益";与“返回迭代(可迭代)" [英] "yield from iterable" vs "return iter(iterable)"

查看:99
本文介绍了“来自可迭代的收益";与“返回迭代(可迭代)"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

包装(内部)迭代器时,通常必须将__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()变体是我到目前为止使用的模式.

解决方案

唯一的区别是,从可迭代对象内部引发异常时会发生什么.使用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

When wrapping an (internal) iterator one often has to reroute the __iter__ method to the underlying iterable. Consider the following example:

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

Is there any significant difference between variant A and B? Variant A returns an iterator object that has been queried via iter() from the internal iterable. Variant B returns a generator object that returns values from the internal iterable. Is one or the other preferable for some reason? In collections.abc the yield from version is used. The return iter() variant is the pattern that I have used until now.

解决方案

The only significant difference is what happens when an exception is raised from within the iterable. Using return iter() your FancyNewClass will not appear on the exception traceback, whereas with yield from it will. It is generally a good thing to have as much information on the traceback as possible, although there could be situations where you want to hide your wrapper.

Other differences:

  • return iter has to load the name iter from globals - this is potentially slow (although unlikely to significantly affect performance) and could be messed with (although anyone who overwrites globals like that deserves what they get).

  • With yield from you can insert other yield expressions before and after (although you could equally use itertools.chain).

  • As presented, the yield from form discards any generator return value (i.e. raise StopException(value). You can fix this by writing instead return (yield from iterator).

Here's a test comparing the disassembly of the two approaches and also showing exception tracebacks: http://ideone.com/1YVcSe

Using 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

Using 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

这篇关于“来自可迭代的收益";与“返回迭代(可迭代)"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆