Closed. This question needs details or clarity。它当前不接受答案。
想改善这个问题吗?添加详细信息并通过editing this post阐明问题。
5年前关闭。
据我所知,监视异常会使程序变慢。
迭代器异常监视器(例如
请注意,范围实际上被视为迭代器。
现在,使用一个简单的生成器函数:
请注意,这里的
同样,没有特定的开销来处理迭代器或生成器(
最后,让我们看一下用
现在,在这里我们可以看到,生成器函数所涉及的指令比该简单迭代器要少一些,主要与实现上的差异有关,而与引起
当然,这些结果基于以下事实:Python for循环将优化迭代器,以消除对
关于为什么以这种方式实现,请参见PEP-234,它定义了迭代器。这专门解决了异常消耗的问题:
有人质疑是否有异常信号表明
迭代并不太昂贵。几种替代方法
已提出StopIteration异常:特殊值End
发出结束信号,函数end()测试迭代器是否
完成,甚至重用IndexError异常。
一个特殊值的问题是,如果序列
包含该特殊值,该序列上的循环将
提前结束而没有任何警告。如果有经验
以null结尾的C字符串并没有教会我们这个问题
会引起想象的Python自省工具麻烦
将遍历所有内置名称的列表,
假设特殊的End值是内置名称!
调用end()函数将需要每个调用两次
迭代。两次通话比一次通话要贵得多
加上异常测试。尤其是时间紧迫的
for循环可以非常便宜地测试异常。
重用IndexError可能会引起混乱,因为它可能是
真正的错误,可以通过结束循环来掩盖
过早地。
想改善这个问题吗?添加详细信息并通过editing this post阐明问题。
5年前关闭。
据我所知,监视异常会使程序变慢。
迭代器异常监视器(例如
StopIteration
)会使for
循环变慢吗? 最佳答案
尽管在通常情况下,异常监视的开销很小,但是在迭代器的情况下,处理StopIteration
异常似乎没有任何开销。 Python将迭代器作为一种特殊情况进行优化,以使StopIteration
不涉及任何异常处理程序。 (我还将观察-可能遗漏了一些东西-很难提出没有隐式使用迭代器的Python for
循环)。
下面是一些示例,首先使用内置的range
函数和简单的for
循环:
Python 2.7.5
>>> import dis
>>> def x():
... for i in range(1,11):
... pass
...
>>> dis.dis(x)
2 0 SETUP_LOOP 23 (to 26)
3 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (1)
9 LOAD_CONST 2 (11)
12 CALL_FUNCTION 2
15 GET_ITER
>> 16 FOR_ITER 6 (to 25)
19 STORE_FAST 0 (i)
3 22 JUMP_ABSOLUTE 16
>> 25 POP_BLOCK
>> 26 LOAD_CONST 0 (None)
29 RETURN_VALUE
请注意,范围实际上被视为迭代器。
现在,使用一个简单的生成器函数:
>>> def g(x):
... while x < 11:
... yield x
... x = x + 1
...
>>> def y():
... for i in g(1):
... pass
...
>>> dis.dis(y)
2 0 SETUP_LOOP 20 (to 23)
3 LOAD_GLOBAL 0 (g)
6 LOAD_CONST 1 (1)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 6 (to 22)
16 STORE_FAST 0 (i)
3 19 JUMP_ABSOLUTE 13
>> 22 POP_BLOCK
>> 23 LOAD_CONST 0 (None)
26 RETURN_VALUE
>>> dis.dis(g)
2 0 SETUP_LOOP 31 (to 34)
>> 3 LOAD_FAST 0 (x)
6 LOAD_CONST 1 (11)
9 COMPARE_OP 0 (<)
12 POP_JUMP_IF_FALSE 33
3 15 LOAD_FAST 0 (x)
18 YIELD_VALUE
19 POP_TOP
4 20 LOAD_FAST 0 (x)
23 LOAD_CONST 2 (1)
26 BINARY_ADD
27 STORE_FAST 0 (x)
30 JUMP_ABSOLUTE 3
>> 33 POP_BLOCK
>> 34 LOAD_CONST 0 (None)
37 RETURN_VALUE
请注意,这里的
y
与上面的x
基本上相同,区别在于一条LOAD_CONST
指令,因为x
引用数字11。同样,我们的简单生成器基本上等效于一段时间内编写的相同内容。环:>>> def q():
... x = 1
... while x < 11:
... x = x + 1
...
>>> dis.dis(q)
2 0 LOAD_CONST 1 (1)
3 STORE_FAST 0 (x)
3 6 SETUP_LOOP 26 (to 35)
>> 9 LOAD_FAST 0 (x)
12 LOAD_CONST 2 (11)
15 COMPARE_OP 0 (<)
18 POP_JUMP_IF_FALSE 34
4 21 LOAD_FAST 0 (x)
24 LOAD_CONST 1 (1)
27 BINARY_ADD
28 STORE_FAST 0 (x)
31 JUMP_ABSOLUTE 9
>> 34 POP_BLOCK
>> 35 LOAD_CONST 0 (None)
38 RETURN_VALUE
同样,没有特定的开销来处理迭代器或生成器(
range
可能比生成器版本有所优化,只是因为它是内置的,而不是由于Python处理它的方式)。最后,让我们看一下用
StopIteration
编写的实际显式迭代器>>> class G(object):
... def __init__(self, x):
... self.x = x
... def __iter__(self):
... return self
... def next(self):
... x = self.x
... if x >= 11:
... raise StopIteration
... x = x + 1
... return x - 1
...
>>> dis.dis(G.next)
7 0 LOAD_FAST 0 (self)
3 LOAD_ATTR 0 (x)
6 STORE_FAST 1 (x)
8 9 LOAD_FAST 1 (x)
12 LOAD_CONST 1 (11)
15 COMPARE_OP 5 (>=)
18 POP_JUMP_IF_FALSE 30
9 21 LOAD_GLOBAL 1 (StopIteration)
24 RAISE_VARARGS 1
27 JUMP_FORWARD 0 (to 30)
10 >> 30 LOAD_FAST 1 (x)
33 LOAD_CONST 2 (1)
36 BINARY_ADD
37 STORE_FAST 1 (x)
11 40 LOAD_FAST 1 (x)
43 LOAD_CONST 2 (1)
46 BINARY_SUBTRACT
47 RETURN_VALUE
现在,在这里我们可以看到,生成器函数所涉及的指令比该简单迭代器要少一些,主要与实现上的差异有关,而与引起
StopIteration
异常有关的几条指令有关。但是,使用此迭代器的函数与上面的y
完全等效:>>> def z():
... for i in G(1):
... pass
...
>>> dis.dis(z)
2 0 SETUP_LOOP 20 (to 23)
3 LOAD_GLOBAL 0 (G)
6 LOAD_CONST 1 (1)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 6 (to 22)
16 STORE_FAST 0 (i)
3 19 JUMP_ABSOLUTE 13
>> 22 POP_BLOCK
>> 23 LOAD_CONST 0 (None)
26 RETURN_VALUE
当然,这些结果基于以下事实:Python for循环将优化迭代器,以消除对
StopIteration
异常的显式处理程序的需要。毕竟,StopIteration
异常本质上是Python for循环操作的正常部分。关于为什么以这种方式实现,请参见PEP-234,它定义了迭代器。这专门解决了异常消耗的问题:
有人质疑是否有异常信号表明
迭代并不太昂贵。几种替代方法
已提出StopIteration异常:特殊值End
发出结束信号,函数end()测试迭代器是否
完成,甚至重用IndexError异常。
一个特殊值的问题是,如果序列
包含该特殊值,该序列上的循环将
提前结束而没有任何警告。如果有经验
以null结尾的C字符串并没有教会我们这个问题
会引起想象的Python自省工具麻烦
将遍历所有内置名称的列表,
假设特殊的End值是内置名称!
调用end()函数将需要每个调用两次
迭代。两次通话比一次通话要贵得多
加上异常测试。尤其是时间紧迫的
for循环可以非常便宜地测试异常。
重用IndexError可能会引起混乱,因为它可能是
真正的错误,可以通过结束循环来掩盖
过早地。
10-05 18:38