Python 3.5:切片与 islice 与替代品?效率对比

Sum*_*ron 4 python list slice

语境

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

my_list[start:end]
Run Code Online (Sandbox Code Playgroud)

但是, slice 生成原始列表的副本(至少是原始列表的引用)。因此,此操作可能比不执行此操作要慢。

islice是替代迭代器的替代方法。由于我只关心将所有值放在一个位置,而不是遍历它们,因此我将不得不进行类型转换:

list(islice(my_list, start, end))
Run Code Online (Sandbox Code Playgroud)

背景工作

为了进行一些比较,我将大小从 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))
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Mar*_*ers 8

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

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

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

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

mylist[start:stop]另一方面,调用仅执行以下操作:

  1. 打电话mylist.__getitem__(slice(start, stop))。此方法直接生成一个新的列表对象,将相同的元素从其内部数组直接复制到新的列表对象数组。