如何切片生成器对象或迭代器?

How to slice a generator object or iterator?

我想遍历迭代器的 "slice"。我不确定这是否可行,因为我知道不可能对迭代器进行切片。我想做的是:

def f():
    for i in range(100):
        yield(i)
x = f()

for i in x[95:]:
    print(i)

这当然失败了:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-37-15f166d16ed2> in <module>()
  4 x = f()
  5 
----> 6 for i in x[95:]:
  7     print(i)

TypeError: 'generator' object is not subscriptable

是否有 pythonic 方式循环遍历生成器的 "slice"?

基本上我真正关心的生成器读取一个非常大的文件并逐行对其执行一些操作。我想测试文件的切片以确保一切按预期执行,但让它 运行 覆盖整个文件非常耗时。

编辑:
如前所述,我需要将其保存在文件中。我希望有一种方法可以用生成器明确指定这一点,例如:

import skbio

f = 'seqs.fna'
seqs = skbio.io.read(f, format='fasta')

seqs 是一个生成器对象

for seq in itertools.islice(seqs, 30516420, 30516432):
    #do a bunch of stuff here
    pass

上面的代码满足了我的需要,但是仍然很慢,因为生成器仍然循环遍历所有行。我希望只循环指定的 slice

islice 是 pythonic 方式

from itertools import islice    

g = (i for i in range(100))

for num in islice(g, 95, None):
    print num

您不能使用普通 slice operations 对生成器对象或迭代器进行切片。 相反,您需要使用 itertools.islice as @jonrsharpe already mentioned in his .

import itertools    

for i in itertools.islice(x, 95)
    print(i)

还要注意 islice returns 一个迭代器并在迭代器或生成器上使用数据。因此,如果您需要回去做某事或使用鲜为人知的 itertools.tee 创建一个 copy ,您将需要将数据转换为列表或创建一个新的生成器对象你的发电机。

from itertools import tee


first, second = tee(f())

一般情况下,answer is itertools.islice,但您应该注意 islice 实际上不会也不能跳过值。它只是在开始 yield 值之前抓取并丢弃 start 值。因此,当您需要跳过很多值 and/or 时,通常最好尽可能避免使用 islice 被跳过的值对 acquire/compute 来说是昂贵的。如果您能找到一种不首先生成值的方法,那就这样做吧。在您的(显然是人为的)示例中,您只需调整 range 对象的起始索引。

在尝试 运行 文件对象的特定情况下,提取大量行(尤其是从慢速介质读取)可能并不理想。假设你不需要特定的行,你可以使用一个技巧来避免实际读取文件的巨大块,同时仍然测试文件的一些距离,是 seek 到一个猜测的偏移量,读出到该行的末尾(丢弃您可能在中间寻找的部分行),然后 islice 从该点开始关闭您想要的许多行。例如:

import itertools

with open('myhugefile') as f:
    # Assuming roughly 80 characters per line, this seeks to somewhere roughly
    # around the 100,000th line without reading in the data preceding it
    f.seek(80 * 100000)
    next(f)  # Throw away the partial line you probably landed in the middle of
    for line in itertools.islice(f, 100):  # Process 100 lines
        # Do stuff with each line

对于文件的特定情况,您可能还想查看 mmap,它可以以类似的方式使用(如果您处理的是数据块而不是文本行,则非常有用,可能在你走的时候随机跳来跳去。

更新: 根据你更新的问题,你需要查看你的 API 文档 and/or 数据格式来弄清楚如何跳过周围适当。看起来像 skbio offers some features for skipping using seq_num, but that's still going to read if not process most of the file. If the data was written out with equal sequence lengths, I'd look at the docs on Alignment; aligned data may be loadable without processing the preceding data at all, by e.g by using Alignment.subalignment to create new Alignments that skip the rest of the data for you.

让我们先澄清一些事情。 Spouse 你想从你的生成器中提取第一个值,基于你在表达式左边指定的参数数量。从这一刻开始,我们遇到了一个问题,因为在 Python 中有两种解包方式。

让我们使用以下示例讨论这些备选方案。假设您有以下列表 l = [1, 2, 3]

1) 第一个选择是不使用 "start" 表达式

a, b, c = l # a=1, b=2, c=3

如果表达式左侧的参数数量(在本例中为 3 个参数)等于列表中的元素数量,这将非常有用。 但是,如果你尝试这样的事情

a, b = l # ValueError: too many values to unpack (expected 2)

这是因为列表包含的参数多于表达式左侧指定的参数

2) 第二种选择是使用 "start" 表达式;这解决了之前的错误

a, b, c* = l #  a=1, b=2, c=[3]

"start" 参数就像一个缓冲区列表。 缓冲区可以有三个可能的值:

    a, b, *c = [1, 2] # a=1, b=2, c=[]
    a, b, *c = [1, 2, 3] # a=1, b=2, c=[3]
    a, b, *c = [1, 2, 3, 4, 5] # a=1, b=2, c=[3,4,5]

请注意,列表必须至少包含 2 个值(在上例中)。如果不是,将引发错误

现在,跳到你的问题。如果您尝试这样的操作:

a, b, c = generator

这仅在生成器仅包含三个值时有效(生成器值的数量必须与左参数的数量相同)。否则,将引发错误。

如果您尝试这样的操作:

a, b, *c = generator
  • 如果生成器中的值个数小于2;将引发错误,因为变量 "a"、"b" 必须具有值
  • 如果生成器中的值个数为3;然后 a=, b=(val_2>, c=[]
  • 如果生成器中的值个数大于3;然后 a=, b=(val_2>, c=[ ... ] 在这种情况下,如果生成器是无限的;该程序将被阻止尝试使用生成器

我为您提出的解决方案如下

# Create a dummy generator for this example
def my_generator():
i = 0
while i < 2:
    yield i
    i += 1

# Our Generator Unpacker
class GeneratorUnpacker:
    def __init__(self, generator):
        self.generator = generator

    def __iter__(self):
        return self

    def __next__(self):
        try:
            return next(self.generator)
        except StopIteration:
            return None # When the generator ends; we will return None as value

if __name__ == '__main__':
    dummy_generator = my_generator()
    g =  GeneratorUnpacker(dummy_generator )
    a, b, c = next(g), next(g), next(g)