python – 为什么要更快地打破而不是提出异常?

在检查了一些简单的测试之后,看起来好像从循环中打破结束生成器而不是提升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

Why is this the case if the standard and accepted method of stopping a generator is using the exception.

只有当发电机没有任何产生任何东西时,才会提出StopIteration异常。而且,它不是中途停止发电机的标准方法。

以下是关于生成器的文档的两个声明,说明如何正确地阻止它们:

> PEP 479 — Change StopIteration handling inside generators

… the proposal also clears up the confusion about how to terminate a
generator: the proper way is return , not raise StopIteration.

> PEP 255 — Simple Generators

Q. Why allow "return" at all? Why not force termination to be spelled
"raise StopIteration"?

A. The mechanics of StopIteration are low-level details, much like the
mechanics of IndexError in Python 2.1: the implementation needs to
do something well-defined under the covers, and Python exposes
these mechanisms for advanced users. That’s not an argument for
forcing everyone to work at that level, though. "return" means “I’m
done” in any kind of function, and that’s easy to explain and to use.
Note that "return" isn’t always equivalent to "raise StopIteration"
in try/except construct, either (see the “Specification: Return”
section).

所以正确的方法是使用return语句,而不是使用break或raise StopIteration。

it seems as if it might be faster to break from a loop to end a generator rather than to raise a StopIteration exception.

的确,这是因为当提出异常时,还有更多的工作要做。您可以使用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
翻译自:https://stackoverflow.com/questions/37154381/why-is-it-faster-to-break-rather-than-to-raise-an-exception

转载注明原文:python – 为什么要更快地打破而不是提出异常?