语境

这是一个关于效率的一般问题。
我有一个列表,我需要一个列表中的连续运行/子列表。通常,这是通过切片完成的:

my_list[start:end]

但是, slice 生成原始列表的副本(至少是原始列表的引用)。因此,此操作可能比不执行此操作要慢。
islice 是一种替代方法,它代替了迭代器。由于我只关心将所有值设为一个,而不是遍历它们,因此我将不得不进行类型转换:
list(islice(my_list, start, end))

背景工作

为了进行一些比较,我将大小从 1 增加到 10,000 的列表随机切片/切片 10 次:
is_vals = []
s_vals = []
for l in range(1, 10000):

    my_list = [random.random() for k in range(l)]

    for p in range(10):
        i = random.randint(0, l)
        j = random.randint(0, l)

        if i < j:

            start_time = time.clock()
            list(islice(my_list, i, j))
            is_vals.append(time.clock() - start_time)
            start_time = time.clock()
            my_list[i:j]
            s_vals.append(time.clock() - start_time)

        else:
            start_time = time.clock()
            list(islice(my_list, j, i))
            is_vals.append(time.clock() - start_time)
            start_time = time.clock()
            my_list[j:i]
            s_vals.append(time.clock() - start_time)

print(statistics.mean(is_vals) - statistics.mean(s_vals))

我发现 slice 仍然更快,islice 和 slice 之间的差异是 2.99e-05。

我不确定,但我会继续把它归结为迭代器对象的类型转换。

问题

有没有比切片更有效的方法来获取列表中的连续运行/子列表?

奖励:有没有办法或多或少地将列表/元组类型转换为切片?例如把 [i,j] 变成 i:j?

最佳答案

你不能在速度上击败 mylist[start:stop],不。如果您想要一个包含来自输​​入列表的连续区域的相同元素的新列表对象,则不是。

这是因为 list 类型实现可以直接访问列表对象的内部存储。您无法从外部更快地访问这些元素。

仅当内存效率很重要时才使用迭代器。迭代器增加了迭代速度的开销,它们通常不会更快。在这种情况下,表达式 list(islice(my_list, start, stop)) 将完成以下工作:

  • my_list 创建一个列表迭代器对象;这将在您迭代时从 my_list 产生元素。
  • 创建一个新的 islice() 迭代器对象;这将从列表迭代器中跳过 start 元素,然后生成值直到到达 stop 索引。
  • islice() 迭代器对象生成迭代器。在这种情况下,只会重用相同的对象,但这仍然是一个单独的 (C) 函数调用。
  • 从步骤 3 中生成的迭代器对象产生的所有元素生成一个新的列表对象。

  • 另一方面, mylist[start:stop] 调用仅执行以下操作:
  • 调用 mylist.__getitem__(slice(start, stop)) 。此方法直接生成一个新的列表对象,将相同的元素从其内部数组直接复制到新的列表对象数组。
  • 关于Python 3.5 : slice vs islice vs alternatives? 效率对比,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41079001/

    10-12 19:55