在对我的应用程序进行基准测试时,我注意到在 Python 中按索引访问数组项相对昂贵,使得 for v in lst: v
明显快于 for i in range(len(lst) : lst[i]
:
from array import array
a_ = array('f', range(1000))
def f1():
a = a_
acc = 0
for v in a:
acc += v
return acc
def f2():
a = a_
acc = 0
for i in range(len(a)):
acc += a[i]
return acc
from dis import dis
from timeit import timeit
for f in f1,f2:
dis(f)
print(timeit(f, number=20000))
print()
制作:
9 0 LOAD_GLOBAL 0 (a_)
3 STORE_FAST 0 (a)
10 6 LOAD_CONST 1 (0)
9 STORE_FAST 1 (acc)
11 12 SETUP_LOOP 24 (to 39)
15 LOAD_FAST 0 (a)
18 GET_ITER
>> 19 FOR_ITER 16 (to 38)
22 STORE_FAST 2 (v)
12 25 LOAD_FAST 1 (acc)
28 LOAD_FAST 2 (v)
31 INPLACE_ADD
32 STORE_FAST 1 (acc)
35 JUMP_ABSOLUTE 19
>> 38 POP_BLOCK
14 >> 39 LOAD_FAST 1 (acc)
42 RETURN_VALUE
0.6036834940023255
17 0 LOAD_GLOBAL 0 (a_)
3 STORE_FAST 0 (a)
18 6 LOAD_CONST 1 (0)
9 STORE_FAST 1 (acc)
19 12 SETUP_LOOP 40 (to 55)
15 LOAD_GLOBAL 1 (range)
18 LOAD_GLOBAL 2 (len)
21 LOAD_FAST 0 (a)
24 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
27 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
30 GET_ITER
>> 31 FOR_ITER 20 (to 54)
34 STORE_FAST 2 (i)
20 37 LOAD_FAST 1 (acc)
40 LOAD_FAST 0 (a)
43 LOAD_FAST 2 (i)
46 BINARY_SUBSCR
47 INPLACE_ADD
48 STORE_FAST 1 (acc)
51 JUMP_ABSOLUTE 31
>> 54 POP_BLOCK
22 >> 55 LOAD_FAST 1 (acc)
58 RETURN_VALUE
1.0093544629999087
循环的核心仅在使用索引访问时存在额外的 LOAD_FAST
BINARY_SUBSCR
操作码时有所不同。然而,这足以让基于迭代器的解决方案比使用索引访问快大约 40%。
不幸的是,在这种形式下,迭代器只能用于读取 输入数组。有没有一种方法可以使用“快速”迭代器来就地更改数组的项目,或者我是否必须坚持使用“慢速”索引访问?
最佳答案
对于完整循环,您可以使用 enumerate
拆分差异,使用索引访问来设置一个值,并使用名称来读取它:
for i, value in enumerate(mysequence):
mysequence[i] = do_stuff_with(value)
不过,您无法避免在一般循环结构中进行索引重新分配; Python 没有等同于 C++ 引用语义,在 C++ 引用语义中,赋值会更改引用的值,而不是重新绑定(bind)名称。
也就是说,如果工作足够简单,list
推导式通过构建新的 list
并批量替换旧的来避免索引的需要:
mysequence[:] = [do_stuff_with(value) for value in mysequence]
分配给 mysequence
的完整切片确保它被修改到位,因此对它的其他引用可以看到更改。如果你不想要这种行为,你可以离开 [:]
(你将重新绑定(bind)到一个新的 list
,没有其他引用)。
关于python - Python 中的可变迭代器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59091550/