语境
这是一个关于效率的一般问题。
我有一个列表,我需要一个列表中的连续运行/子列表。通常,这是通过切片完成的:
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) 函数调用。 另一方面,
mylist[start:stop]
调用仅执行以下操作:mylist.__getitem__(slice(start, stop))
。此方法直接生成一个新的列表对象,将相同的元素从其内部数组直接复制到新的列表对象数组。 关于Python 3.5 : slice vs islice vs alternatives? 效率对比,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41079001/