Python 3.5:切片vs islice vs替代品?效率比较

时间:2016-12-10 18:41:21

标签: python list slice

上下文

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

 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?

2 个答案:

答案 0 :(得分:2)

你无法在速度上击败mylist[start:stop],没有。如果您希望新列表对象包含来自输​​入列表的连续区域的相同元素,则不会。

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

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

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

    1. 致电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))

也会创建新列表,因此您看不到切片的任何优势。