为什么破解而不是提出例外更快?

时间:2016-05-11 06:12:58

标签: python python-3.x

在检查了几个简单的测试后,似乎从循环中断到结束生成器而不是引发StopIteration异常似乎更快。如果停止生成器的标准和可接受的方法正在使用异常,为什么会出现这种情况。 source

In [1]: def f():
   ....:     for i in range(1024):
   ....:         yield None
   ....:         break
   ....:     

In [2]: def g():
   ....:     for i in range(1024):
   ....:         yield None
   ....:         raise StopIteration
   ....:     

In [3]: %timeit for i in f(): pass
1000000 loops, best of 3: 1.22 µs per loop

In [4]: %timeit for i in g(): pass
100000 loops, best of 3: 5.9 µs per loop

In [5]: %timeit for i in f(): pass
1000000 loops, best of 3: 1.22 µs per loop

In [6]: %timeit for i in g(): pass
100000 loops, best of 3: 5.82 µs per loop

2 个答案:

答案 0 :(得分:28)

  

如果停止生成器的标准和可接受的方法正在使用异常,为什么会这样呢。

仅当生成器不再生成任何内容时才会引发异常StopIteration。并且,它不是中途停止发电机的标准方式。

以下是有关如何正确阻止它们的生成器文档中的两个声明:

  1. PEP 479 -- Change StopIteration handling inside generators
  2.   

    ......该提案还清除了关于如何终止a的混淆        生成器:正确的方法是return,而不是raise StopIteration

    1. PEP 255 -- Simple Generators
    2.   

      Q值。为什么要允许"return"?为什么不强制终止拼写       "raise StopIteration"

           

      一个。 StopIteration的机制是低级细节,非常类似于          Python 2.1中的IndexError机制:实现需要          在隐藏的内容中定义某些,并公开Python          这些机制适合高级用户。这不是一个论据          然而,迫使每个人都在这个层面上工作。 "return"表示"我是          完成"在任何类型的功能,这很容易解释和使用。          请注意,"return"并不总是等同于"raise StopIteration"          在try / except构造中,(参见"规范:返回"          部分)。

      因此,正确的方法是使用return语句,而不是使用breakraise StopIteration

        

      似乎从循环中break更快结束生成器而不是引发StopIteration异常。

      确实是因为在提出异常时还有更多的工作要做。您可以使用dis模块查看字节码:

      In [37]: dis.dis(f)
        2           0 SETUP_LOOP              26 (to 29)
                    3 LOAD_GLOBAL              0 (range)
                    6 LOAD_CONST               1 (1024)
                    9 CALL_FUNCTION            1
                   12 GET_ITER            
              >>   13 FOR_ITER                12 (to 28)
                   16 STORE_FAST               0 (i)
      
        3          19 LOAD_CONST               0 (None)
                   22 YIELD_VALUE         
                   23 POP_TOP             
      
        4          24 BREAK_LOOP          
                   25 JUMP_ABSOLUTE           13
              >>   28 POP_BLOCK           
              >>   29 LOAD_CONST               0 (None)
                   32 RETURN_VALUE        
      
      In [38]: dis.dis(g)
        2           0 SETUP_LOOP              31 (to 34)
                    3 LOAD_GLOBAL              0 (range)
                    6 LOAD_CONST               1 (1024)
                    9 CALL_FUNCTION            1
                   12 GET_ITER            
              >>   13 FOR_ITER                17 (to 33)
                   16 STORE_FAST               0 (i)
      
        3          19 LOAD_CONST               0 (None)
                   22 YIELD_VALUE         
                   23 POP_TOP             
      
        4          24 LOAD_GLOBAL              2 (StopIteration)
                   27 RAISE_VARARGS            1
                   30 JUMP_ABSOLUTE           13
              >>   33 POP_BLOCK           
              >>   34 LOAD_CONST               0 (None)
                   37 RETURN_VALUE
      

      您可以看到几乎所有内容都相同但是为了引发异常,它必须执行一些额外的指令:

      24 LOAD_GLOBAL              2 (StopIteration)
      27 RAISE_VARARGS            1
      

答案 1 :(得分:4)

如果你做了休息,那么它只是退出循环,在你的情况下没有更多的事情要做,所以它也退出了这个功能。 如果你引发异常,那么它仍然必须处理后台发生的所有异常事件。

more work == more execution time

可以使用StopIteration的原因(我从来没有使用过那个,所以不要引用我)是为了以后捕获它,或者只是退出嵌套循环。 我只是快速查看了您提供的源代码,在我看来,StopIteration仅用于说明生成器如何工作,但它不用于生成器。

我希望能回答你的问题。