python - Python 中的可变迭代器?

标签 python python-3.x loops optimization

在对我的应用程序进行基准测试时,我注意到在 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/

相关文章:

python - 使用 matplotlib Python 绘制参数图

python - Scrapy管道到mysql错误1241

python - 如何找到python中有多少个单词?

python - 从 python 列表创建子列表

带有 map 的 Javascript ES6 Promises

c - 在 C 中将数组的内容传输到另一个数组

javascript - 动态创建的 HTML 表格打开新页面

python - 使用生成器编写文件头和文件体是pythonic吗?

Python Tkinter - 在初始声明后更改 Canvas 大小

python - 如何有效地计算字符串中字符频率的前缀和?