获取Python中无限生成器的子集列表

时间:2014-04-26 01:18:49

标签: python generator enumerate

摘要:我正在尝试了解 itertools.islice


我正在尝试找到一个最佳方法来获取由无限生成器函数返回的子集组成的列表。例如,我可能想要一个生成器的第1000到第2000项的列表。

这是我的示例生成器:

def infinite_counter():
    i = 0
    while True:
        i += 2
        yield i

这些值是我希望列表开始和停止的生成器的返回索引:

start = 1000
end = 2000

方法1 :列表理解(失败)

[val for ind,val in enumerate(infinite_counter()) if start <= ind <= end ]

当你扩展到这个时,这显然永远不会回归:

for ind, val in enumerate(infinite_counter()):
    if start < ind < end:
       val

方法2 :list()(正常)

list(next(iter([])) if ind > end else val for ind,val in enumerate(infinite_counter()) if ind >= start)

这样可行,但真的感觉像是黑客。它也很难遵循,但我错误地认为它会比方法3更快。

方法3 :简单方法(工作)

my_list = []
for ind,val in enumerate(infinite_counter()):
    if ind >= start:
        my_list.append(val)
        if ind >= end:
            break

这是我想到这样做的第一种方式,然后我责备自己不是pythonic。我很惊讶这与时间方法2几乎完全一样。

方法4 :itertools.takewhile (工作)

[val for ind,val in itertools.takewhile(lambda tup: tup[0] < end, enumerate(infinite_counter())) if ind > start]

起初,我认为takewhile没有用,因为我把lambda称为“lambda ind,val:”。但它给lambda提供了两个值的元组。我只需要将元组中的第一个术语作为提前退出的索引。这比方法2和3慢,几乎和方法5一样慢。

方法5 :包装生成器(正常)

def top_ending_generator(end):
    for ind,val in enumerate(infinite_counter()):
        if ind > end:
            break
        yield ind,val

[val for ind,val in top_ending_generator(end) if ind > start]

正如预期的那样,这比方法2和3慢得多。

总的来说,我很惊讶地看到方法3的时间非常接近方法2的时间。这是更多的代码,但更容易让人关注。这是我现在实现的方式

我还应该考虑其他方法或更好的解决方法吗?

编辑:

方法6 itertools.islice (获胜者)

list(itertools.islice(infinite_counter(), start, end))

这比我最初的具有列表推导的itertools.islice解决方案稍快一些:

[val for val in itertools.islice(infinite_counter(), start_ind, end_ind)]

惊人的是找到正确的方法。

对于那些得分,我的时间发现如下:

方法6 =单位时间

方法2~ = 2.5 *单位时间

方法3~ = 3 *单位时间

方法4~ = 4.2 *单位时间

方法5~ = 4 *单位时间

1 个答案:

答案 0 :(得分:3)

from itertools import islice

list(islice(infinite_counter(), 1000, 2000))

注意这个

list(next(iter([])) if ind > end else val for ind,val in enumerate(infinite_counter()) if ind >= start)

转换为此

def _secret():
    for ind, val in enumerate(infinite_counter()):
        if ind >= start:
            if ind > end:
                yield list(next(iter([])))

            else:
                yield val

list(_secret())

很容易改进

def _secret():
    for ind, val in enumerate(infinite_counter()):
        if ind < start:
            continue

        if ind > end:
            break

        yield val

list(_secret())

对我来说很好看。