为什么破解而不是提出异常更快?

Tan*_*bot 20 python python-3.x

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

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
Run Code Online (Sandbox Code Playgroud)

AKS*_*AKS 28

如果停止生成器的标准和可接受的方法正在使用异常,为什么会出现这种情况.

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

以下是有关如何正确停止它们的生成器文档中的两个语句:

  1. PEP 479 - 更改生成器内的StopIteration处理:

...该提案还清除了关于如何终止发电机的困惑:正确的方法是return,而不是raise StopIteration.

  1. PEP 255 - 简单的发电机

问:为什么要允许"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
Run Code Online (Sandbox Code Playgroud)

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

24 LOAD_GLOBAL              2 (StopIteration)
27 RAISE_VARARGS            1
Run Code Online (Sandbox Code Playgroud)