【问题标题】:Update a python list given a list of indexes给定索引列表更新 python 列表
【发布时间】:2022-01-24 18:35:41
【问题描述】:

给定要更新的索引列表,如何更新列表。

例如,假设我们有一个列表

l = ['4', '5', '8', '19', '2', '53', '125']

indexes = [0, 1, 4, 5]

假设我们要将这些索引更新为int。所需的更新列表将是

l = [4, 5, '8', '19', 2, 53, '125']

是的,我们可以通过如下循环轻松完成:

for i in indexes:
    l[i] = int(l[i])

但实际上,这样的事情要快一些

l[0] = int(l[0])
l[1] = int(l[1])
l[4] = int(l[4])
l[5] = int(l[5])

这里的速度差异可能不会很明显,因为只有 4 个项目,但是一旦有 40 个或更多,您就可以看到差异。但是写这么多重复语句也很痛苦。

有没有办法对此进行更多优化,也许使用map 并生成一个列表来更新列表(仅基于特定索引或类似的东西)

【问题讨论】:

  • 如果索引列表是动态的,那么循环是唯一的方法。
  • 你用更大的列表实际测量过速度差异吗?我认为这将是微不足道的。将字符串转换为 int 的成本远远超过遍历列表的成本。
  • 请注意 PEP 8 specifically lists 小写“L”作为 Python 中要避免的名称。
  • 我看到循环增加了约 10%(与无循环版本相比),使用 timeit 并更新列表中的大约 40 个项目
  • 老实说,您在这里犯了过早的优化。 for 循环代码更易于编写、更易于阅读,并且可以处理任意列表。更新时间还不到最初创建列表时间的一半。像这样的代码几乎永远不会成为关键路径。

标签: python list optimization


【解决方案1】:
[int(l[i]) if i in indexes else l[i] for i in range(len(l))]
# [4, 5, '8', '19', 2, 53, '125']
%timeit [int(l[i]) if i in indexes else l[i] for i in range(len(l))]
2.82 µs ± 566 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

【讨论】:

  • 这是一种生成全新列表的非常有效的方法,但是 OP 询问的是就地替换(这不需要在内存中构建一个全新的列表,占用两倍空间)。
  • @Grismar IMO 的瓶颈仅仅是 Python 读取和写入索引的速度很慢,所以这里唯一可行的选择是创建第二个列表,至少在我有限的知识范围内.
  • 在特定情况下,替换列表可能是时间性能更好的解决方案(尽管显然永远不会用于空间),但我只是指出这不是 OP 所要求的。不过,您可能想检验您的假设 - 看看需要发生什么,我实际上怀疑您的解决方案实际上更快。
【解决方案2】:

将列表元素替换 40 次这样简单的事情并不能真正测试它的大规模性能。请考虑一下:

from timeit import timeit
from random import shuffle


def f(xs, indices):
    for i in indices:
        xs[i] = int(xs[i])


def main():
    for p in range(6):
        n = 10**(p+3)
        shuffle(indices := list(range(n)))
        indices = [indices.pop() for __ in range(n // 100)]
        xs = [str(x) for x in range(n)]
        print(n, timeit(lambda: f(xs, indices), number=1) / n)


main()

结果:

1000 2.799999999997249e-09
10000 1.4999999999987245e-09
100000 2.817999999999987e-09
1000000 3.4772999999999053e-09
10000000 3.8900799999999515e-09
100000000 5.486388000000062e-09

(这显示了在单次迭代中平均花费的时间,对于长度增加的列表)

原始列表的大小以非平凡的方式影响性能。另外,考虑到只有替换指令的代码必须来自某个地方,并且由于您可能不会为大型列表手动编写所有代码,因此您需要生成它 - 并且该代码保证比实际循环。

所以要么你只需要它来进行少量更新,在这种情况下你是对的,写出指令会更快 - 但会使你的代码更大(影响加载时间和可用内存)。或者你需要这个来进行大量的更新,它要么不值得,因为没有有效的方法来生成代码作为替代 - 或者你可以在其他地方生成代码,也可以牺牲时间和加载更大的程序所需的空间,唯一的好处是循环速度略快。

但是,这似乎不太可能是一个值得首先考虑的优化。这里的收益永远不会超过几分之一秒,对于非常大的列表最多只有几秒钟 - 您的代码中必须有无数其他地方可以带来更大的收益。

最后,如果您在这里坚持速度很重要,您应该考虑使用比字符串列表或 Python 之类的语言更好的数据结构来完成这项工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-08
    • 2014-04-20
    • 2013-03-28
    • 2017-08-30
    • 2010-11-22
    • 2015-07-25
    相关资源
    最近更新 更多