早些时候,我试图回答一个问题,我希望尽可能有效地迭代列表切片。
for x in lst[idx1:]:
不理想,因为它会创建副本(通常,这是O(n)
)。我的下一个想法是使用itertools.islice
。但是,如果您查看文档,islice
似乎会调用next
,直到找到它正在查找的索引,它将开始产生值。这也是O(n)
。如果传递给islice
的对象是list
或tuple
,似乎可以在此处进行优化 - 似乎您可以直接迭代“切片”(在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???
答案 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级重写这样的功能,但我没有看到很多优点来做到这一点。