itertools.islice实现 - 有效地切片列表

时间:2012-11-29 15:19:48

标签: python slice itertools

早些时候,我试图回答一个问题,我希望尽可能有效地迭代列表切片。

for x in lst[idx1:]:

不理想,因为它会创建副本(通常,这是O(n))。我的下一个想法是使用itertools.islice。但是,如果您查看文档,islice似乎会调用next,直到找到它正在查找的索引,它将开始产生值。这也是O(n)。如果传递给islice的对象是listtuple,似乎可以在此处进行优化 - 似乎您可以直接迭代“切片”(在C)没有实际制作副本。我很好奇这个优化是否在the source,但我没有找到任何东西。我对C和python源代码树并不是很熟悉,所以我完全有可能错过它。

我的问题是:

  

有没有办法迭代列表“切片”而不需要复制列表切片而不烧掉一堆不需要的元素(在优化的C实现中)?

我很清楚我可以为此编写自己的生成器(非常天真,不考虑许多参数应该是可选的这一事实等):

def myslice(obj,start,stop,stride):
    for i in xrange(start,stop,stride):
        yield obj[i]

但这肯定不会超过优化的C实现。


如果您想知道为什么我需要这个而不是直接在切片上循环,请考虑以下区别:

takewhile(lambda x: x == 5, lst[idx:])  #copy's the tail of the list unnecessarily

takewhile(lambda x: x == 5, islice(lst,idx,None)) #inspects the head of the list unnecessarily 

最后:

takewhile(lambda x: x == 5, magic_slice(lst,idx,None)) #How to create magic_slice???

4 个答案:

答案 0 :(得分:4)

我认为值得一提的是NumPy切片是非复制的(它们在底层数组上创建了一个视图)。因此,如果您可以将NumPy数组用于您的数据,那么这将解决问题。最重要的是,您可以通过矢量化获得额外的性能提升。

答案 1 :(得分:2)

  

有没有办法迭代列表“切片”而不需要复制列表切片而不烧掉一堆不需要的元素(在优化的C实现中)?

是的,如果你写了C实现。 Cython使这一点变得特别容易。

cdef class ListSlice(object):
    cdef object seq
    cdef Py_ssize_t start, end

    def __init__(self, seq, Py_ssize_t start, Py_ssize_t end):
        self.seq = seq
        self.start = start
        self.end = end

    def __iter__(self):
        return self

    def __next__(self):
        if self.start == self.end:
            raise StopIteration()
        r = self.seq[self.start]
        self.start += 1
        return r

答案 2 :(得分:1)

如果你使用PyPy(你可能因为关心性能),他们会优化字符串切片为非复制:http://doc.pypy.org/en/latest/interpreter-optimizations.html

答案 3 :(得分:0)

islice是来自itertools模块的函数,因此它通常与iterator一起使用(并且肯定应该有效),而不仅仅是list s。因此,您无法在itertools源代码中找到优化,因为它应该适用于任何给定的迭代器。

您的案例中的正确方法是:

def magic_slice(lst, start, end=None):
    for pos in xrange(start, (end or len(lst)):
        yield lst[pos]

takewhile将“逐个”调用您的生成器,它将yield新值 - 与通用列表行走+ xrange迭代相同的“速度”。因此,这种实现的开销很小。如果你需要更多 - 你可以在C级重写这样的功能,但我没有看到很多优点来做到这一点。