这是关于效率的一般性问题。 我有一个列表,我需要从列表中连续运行/子列表。通常,这是通过切片完成的:
doSomethingWithDefaultFormatInstance(dateFormat.value)
但是,slice会生成原始列表的副本(至少是原始列表的引用)。因此,此操作可能比不执行此操作的速度慢。
public function loginAction(Request $request) {
$authenticationUtils = $this->get('security.authentication_utils');
$error = $authenticationUtils->getLastAuthenticationError();
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('AppBundle:Security:login.html.twig', array(
'last_username' => $lastUsername
, 'error' => $error
));
}
是另一种替代它的迭代器。因为我只关心所有的值都在一个,而不是迭代它们,我将不得不输入强制转换:
my_list[start:end]
为了进行一些比较,我在1到10,000的增加大小的列表上随机切片/分割10次:
islice
我发现切片仍然更快,islice和切片之间的差异为2.99e-05。
我不确定,但我会继续并将其归结为类型化迭代器对象。
有没有比切片更有效的方法来获得列表中的连续运行/子列表?
Bonus:有没有办法将列表/元组或多或少地转换为切片?例如把[i,j]变成i:j?
答案 0 :(得分:2)
你无法在速度上击败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))
。此方法直接生成一个新的列表对象,其中相同的元素从其内部数组直接复制到新的列表对象数组。答案 1 :(得分:0)
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略快于切片。这是因为Python解释器创建了一个新列表(my_list [i:j]),然后在行中迭代它
for k in my_list[i:j]:
而在行
for k in islice(my_list, i, j):
它不会创建新列表,而是直接从my_list到第i个索引迭代my_list。但是,当你写
list(islice(my_list, i, j))
也会创建新列表,因此您看不到切片的任何优势。