为什么我要使用itertools.islice而不是普通的列表切片?

36

在我的看来,itertools 模块中的许多函数都有更简单的等效函数。例如,据我所知,itertools.islice(range(10),2,5)range(10)[2:5] 执行的是相同的操作,而 itertools.chain([1,2,3],[4,5,6])[1,2,3]+[4,5,6] 也是相同的。主要文档页面提到了速度优势,但除此之外,选择 itertools 的理由是什么呢?


它们实际上并不做相同的事情。itertools.islice()、range()和itertools.chain()返回不同的对象。最终这些对象的行为相同,但在您的情况下,如果速度很重要,我会考虑比较字节码。 - HelloWorld
3个回答

51

针对你提出的这两个例子:

import itertools


data1 = range(10)

# This creates a NEW list
data1[2:5]

# This creates an iterator that iterates over the EXISTING list
itertools.islice(data1, 2, 5)


data2 = [1, 2, 3]
data3 = [4, 5, 6]

# This creates a NEW list
data2 + data3

# This creates an iterator that iterates over the EXISTING lists
itertools.chain(data2, data3)

使用迭代器而不是其他方法有许多原因。如果列表非常大,创建包含大的子列表的新列表或者尤其是创建一个两个其他列表副本的列表可能会成为问题。使用 islice() 或者 chain() 允许你按照你想要的方式迭代列表,而无需使用更多内存或计算来创建新列表。此外,正如 unutbu提到的那样,使用迭代器时不能使用括号切片或加法。

我希望这已经足够回答了,还有很多其他答案和资源解释为什么迭代器很棒,所以我不想在这里重复所有的信息。


值得一提的是,itertools.chain 可以接受多个参数,将任意数量的列表链接在一起,而使用 + 运算符则无法实现。例如:itertools.chain(*arbitrary_list_of_lists) - veggie1
@veggie1,你可以使用+来连接多个列表。不过最好还是使用itertools.chain(),或者如果需要存储的话,可以使用list.extend()来构建一个列表。 - Cyphase
可以这样做,但除非你使用带有operator.add的循环,否则不能为任意或未知数量的列表执行此操作。 - veggie1
3
@veggie1,好的... sum(([1, 2, 3], [4, 5, 6], [7, 8, 9]), [])。或者,sum(arbitrary_list_of_lists, [])。 :D - Cyphase
@veggie1,不错的观点,我明白你的意思。我并不是故意要固执:P。 - Cyphase
哦,我没意识到你可以用 sum 这个函数做那件事。谢谢! - veggie1

37

itertools.islice可以对迭代器进行切片操作。 只有序列支持索引操作。 例如,

In [64]: iterator = (x**2 for x in range(10))

In [65]: list(IT.islice(iterator, 2, 5))
Out[65]: [4, 9, 16]

In [66]: iterator[2:5]
TypeError: 'generator' object has no attribute '__getitem__'

2
此外,即使对于列表,islice也不会创建新的列表,而常规的列表切片则会。 - BrenBarn
另一方面,islice将在列表中迭代通过0..start元素,而列表切片跳过它们并直接到达start。也许一个替代方案是(xs[i] for i in range(start, stop)) - Mateen Ulhaq

-1

你可以使用原生的Python来完成它

In [64]: iterator = (x**2 for x in range(10))

In [65]: [x for i, x in enumerate(iterator) if i>=2 and i<5]
Out[65]: [4, 9, 16]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接