在 Python 中索引和切片生成器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2322642/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Index and Slice a Generator in Python
提问by Jason Christa
Lets say I have a generator function that looks like this:
假设我有一个如下所示的生成器函数:
def fib():
x,y = 1,1
while True:
x, y = y, x+y
yield x
Ideally, I could just use fib()[10]or fib()[2:12:2]to get indexes and slices, but currently I have to use itertools for these things. I can't use a generator for a drop in replacement for lists.
理想情况下,我可以只使用fib()[10]或fib()[2:12:2]来获取索引和切片,但目前我必须使用 itertools 来处理这些事情。我不能使用生成器来替代列表。
I believe the solution will be to wrap fib()in a class:
我相信解决方案是将fib()包装在一个类中:
class Indexable(object):
....
fib_seq = Indexable(fib())
What should Indexablelook like to make this work?
Indexable应该是什么样子来完成这项工作?
回答by unutbu
import itertools
class Indexable(object):
def __init__(self,it):
self.it = iter(it)
def __iter__(self):
return self.it
def __getitem__(self,index):
try:
return next(itertools.islice(self.it,index,index+1))
except TypeError:
return list(itertools.islice(self.it,index.start,index.stop,index.step))
You could use it like this:
你可以这样使用它:
it = Indexable(fib())
print(it[10])
#144
print(it[2:12:2])
#[610, 1597, 4181, 10946, 28657]
Notice that it[2:12:2]
does not return [3, 8, 21, 55, 144]
since the iterator had already advanced 11 elements because of the call to it[10]
.
请注意,由于调用 ,迭代器已经推进了 11 个元素,因此it[2:12:2]
不会返回。[3, 8, 21, 55, 144]
it[10]
Edit:If you'd like it[2:12:2]
to return [3, 8, 21, 55, 144]
then perhaps use this instead:
编辑:如果您想it[2:12:2]
返回,[3, 8, 21, 55, 144]
则可以改用它:
class Indexable(object):
def __init__(self, it):
self.it = iter(it)
self.already_computed = []
def __iter__(self):
for elt in self.it:
self.already_computed.append(elt)
yield elt
def __getitem__(self, index):
try:
max_idx = index.stop
except AttributeError:
max_idx = index
n = max_idx - len(self.already_computed) + 1
if n > 0:
self.already_computed.extend(itertools.islice(self.it, n))
return self.already_computed[index]
This version saves the results in self.already_computed
and uses those results
if possible. Otherwise, it computes more results until it has sufficiently many
to return the indexed element or slice.
self.already_computed
如果可能,此版本将结果保存并使用这些结果。否则,它会计算更多的结果,直到它有足够多的结果来返回索引元素或切片。
回答by M. Alekseev
To slice generator you can use islice
function from itertools
要切片生成器,您可以使用以下islice
函数itertools
from itertools import islice
for i in islice(generator, 5):
# Will be taken first 5 elems
for i in islice(generator, 5, None):
# Will be taken everything starting at 5th
回答by Jason Christa
So based off the code from ~unutbu and adding in a little itertools.tee:
所以基于 ~unutbu 的代码并添加一点 itertools.tee:
import itertools
class Indexable(object):
def __init__(self, it):
self.it = it
def __iter__(self):
self.it, cpy = itertools.tee(self.it)
return cpy
def __getitem__(self, index):
self.it, cpy = itertools.tee(self.it)
if type(index) is slice:
return list(itertools.islice(cpy, index.start, index.stop, index.step))
else:
return next(itertools.islice(cpy, index, index+1))
回答by Wolph
If it's a 1-use slice than you could simply use the method written by ~unutbu. If you need to slice multiple times you would have to store all the intermediate values so you can "rewind" the iterator. Since iterators can iterate anything it would not have a rewind method by default.
如果它是 1 次使用的切片,那么您可以简单地使用 ~unutbu 编写的方法。如果您需要多次切片,则必须存储所有中间值,以便可以“倒带”迭代器。由于迭代器可以迭代任何东西,所以默认情况下它没有倒带方法。
Also, since a rewinding iterator has to store every intermediate result it would (in most cases) have no benefit over simply doing list(iterator)
此外,由于倒带迭代器必须存储每个中间结果,因此它(在大多数情况下)比简单地执行没有任何好处 list(iterator)
Basically... you either don't need an iterator, or you're not specific enough about the situation.
基本上......你要么不需要迭代器,要么你对情况不够具体。
回答by John La Rooy
Here is ~unutbu's answer modified to subclass list. Obviously abuse such as append
, insert
etc. will produce weird results!
这是修改为子类列表的 ~unutbu 的答案。显然,诸如append
, 之类的滥用insert
会产生奇怪的结果!
you do get __str__
and __repr__
methods for free though
你确实免费获得__str__
和__repr__
方法
import itertools
class Indexable(list):
def __init__(self,it):
self.it=it
def __iter__(self):
for elt in self.it:
yield elt
def __getitem__(self,index):
try:
max_idx=index.stop
except AttributeError:
max_idx=index
while max_idx>=len(self):
self.append(next(self.it))
return list.__getitem__(self,index)