【问题标题】:Get list of subset from infinite generator in Python从 Python 中的无限生成器获取子集列表
【发布时间】:2014-06-11 21:29:07
【问题描述】:

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

方法二: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

这是我第一个想到的方法,在我责备自己不是蟒蛇之前。令我惊讶的是,这在时间上与方法 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 * 单位时间

【问题讨论】:

    标签: python generator enumerate


    【解决方案1】:
    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())
    

    我觉得不错。

    【讨论】:

    • 找到正确的方法可以做到的事情令人惊讶。谢谢。总是很高兴为看起来很讨厌的事情找到正确的解决方案,无论您如何解决它。
    猜你喜欢
    • 2021-01-27
    • 2010-12-04
    • 2019-04-22
    • 1970-01-01
    • 1970-01-01
    • 2018-10-04
    • 2016-04-14
    • 1970-01-01
    • 2014-01-21
    相关资源
    最近更新 更多