Python 3.5:slice vs islice vs alternatives?效率比较

Python 3.5: slice vs islice vs alternatives? Efficiency comparison

上下文

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

my_list[start:end]

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

islice 是一种替代方法,它代替了迭代器。由于我只关心将所有值集中在一起,而不是对它们进行迭代,因此我将不得不键入 cast:

list(islice(my_list, start, end))

背景工作

为了做一些比较,我在列表中随机 sliced/isliced 10 次,列表的大小从 1 增加到 10,000:

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。

我不确定,但我会继续将其归结为对迭代器对象进行类型转换。

问题

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

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

你无法在速度上击败 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))。此方法直接生成一个新的列表对象,其中的元素从其内部数组直接复制到新的列表对象数组。
import random
import time
from itertools import islice
import statistics

l = 1000000
is_vals, s_vals = [], []
my_list = [random.random() for _ in range(l)]
for p in range(10):
    i = random.randint(0, l//3)
    j = random.randint(l-l//3, l)

    start_time = time.clock()
    sum1 = 0
    for k in islice(my_list, i, j):
        sum1 += k
    is_vals.append(time.clock() - start_time)
    start_time = time.clock()
    sum2 = 0
    for k in my_list[i:j]:
        sum2 += k
    s_vals.append(time.clock() - start_time)
    assert sum1 == sum2

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

这表明 islice 比 slice 稍快。这是因为 Python 解释器创建了一个新列表 (my_list[i:j]),然后在行

中对其进行迭代
for k in my_list[i:j]:

而在行

for k in islice(my_list, i, j):

它不创建新列表,直接从第 i 个索引到第 j 个索引遍历 my_list。然而,当你写

list(islice(my_list, i, j))

同时创建了新列表,因此您看不出有什么比 slice 更好的地方。