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

摘要:我正在尝试了解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

这是我在避免不被Python欺骗之前自我思考的第一种方法.令我惊讶的是,它的计时几乎与方法2完全相同.

方法4:itertools.takewhile(工作)

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

起初,我以为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 *单位时间

解决方法:

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())

在我看来,这很好.

上一篇:Python enumerate() 函数


下一篇:LeetCode做题记录(Python版)