首页 > 解决方案 > 流生成器。详细使用迭代器

问题描述

我试图弄清楚迭代器如何与这个例子一起工作:

有一个函数为给定的可迭代对象(列表、生成器等)生成流生成器,其元素包含位置和值并按外观顺序排序。流生成器等于初始流(没有位置),间隙用零填充。

from itertools import count

def gen_stream(total, sorted_iterable, extractor=lambda x: x):
    sorted_iterator = iter(sorted_iterable)
    iterable = count() if total is None else range(total)
    try:
        current_extracted_record = extractor(next(sorted_iterator))
    except StopIteration:
        current_extracted_record = None
    for i in iterable:
        if current_extracted_record:
            if i == current_extracted_record[0]:
                try:
                    yield current_extracted_record[1]
                    current_extracted_record = extractor(next(sorted_iterator))
                except StopIteration:
                    current_extracted_record = None
            else:
                yield 0
        else:
            yield 0

例如:

gen = gen_stream(9,[(4,111),(7,12)])
list(gen) 
[0, 0, 0, 0, 111, 0, 0, 12, 0] # first element has zero index, so 111 located on fifth position, 12 located on 8th position

此功能还支持自定义位置值提取器,用于更高级的情况,例如

def day_extractor(x):
    months = [31, 28, 31, 30, 31, 31, 30, 31, 30, 31, 30, 31]
    acc = sum(months[:x[1] - 1]) + x[0] - 1
    return acc, x[2]
precipitation_days = [(3,1,4),(5,2,6)]
list(gen_stream(59,precipitation_days,day_extractor)) #59: January and February to limit output
[0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

precipitation_days 格式如下:(d,m,mm),其中 d - 月中的天数,m - 月,mm - 以毫米为单位的降水量因此,例如:

(3,1,4) # January,3 precipitation: 4 mm
(5,2,6) # February,5 precipitation: 6 mm

提取器作为带有默认值的可选第三个参数传递 - 处理(位置,值)对的 lambda 函数,如第一个示例中所示。

这是问题开始的地方

问题1 我可以更换吗

    try:
        current_extracted_record = extractor(next(sorted_iterator))
    except StopIteration:
        current_extracted_record = None

使用函数next的默认值,而不是通过使用一行代码来捕获异常 StopIteration

current_extracted_record = extractor(next((sorted_iterator), None))

它在其他情况下总是能正常工作吗?

问题 2如何通过使用next()方法的默认值和循环不是循环for来替换这部分。理论上,代码应该更短。

    for i in iterable:
        if current_extracted_record:
            if i == current_extracted_record[0]:
                try:
                    yield current_extracted_record[1]
                    current_extracted_record = extractor(next(sorted_iterator))
                except StopIteration:
                    current_extracted_record = None
            else:
                yield 0
        else:
            yield 0

问题 3这似乎是一个愚蠢的问题,但据我了解,提取器没有索引。那么方括号中的数字是什么意思呢?

current_extracted_record[0] 
current_extracted_record[1]

谢谢,如果你能帮忙。

对于线程中的 3 个问题,我深表歉意,但在我看来,它们以不同的细节描述了相同的问题。

答案(问题1&问题2)

def gen_stream(total, sorted_iterable, extractor=lambda x: x):
    elem_iter = iter(map(extractor, sorted_iterable))
    pos, val = next(elem_iter, (None, None))
    cnt = 0
    while total is None or cnt < total:
        if cnt == pos:
            yield val
            pos, val = next(elem_iter, (None, None))
        else:
            yield 0
        cnt += 1

标签: pythonfunctionloopsiteratorgenerator

解决方案


我会将整个功能减少到

from itertools import count, islice, chain, repeat


def gen_stream(sorted_iterable, *, extractor=lambda x: x, total=None):
    itr = chain(map(extractor, sorted_iterable), repeat(None))

    current = next(itr)
    for i in islice(count(), total):
        if current is None or i != current[0]:
            yield 0
        else:
            yield current[1]
            current = next(itr)

我们创建一个迭代器,其元素是extractor应用于原始迭代的返回值,然后根据None需要持续。无需StopIteration明确检查。

如果totalNone,则islice(count(), total)等价于count(); 否则,它等价于range(total)


或者,

def gen_stream(sorted_iterable, *, extractor=lambda x: x, total=None):
    def stream():
        pos = 0
        for new_pos, value in map(extractor, sorted_iterable):
            yield from repeat(0, new_pos - pos)
            yield value
            pos = new_pos + 1
    yield from islice(chain(stream(), repeat(0)), total)

内部生成器将用 0 修补间隙,然后最终yield from生成所需的流。


在每种情况下,我都将两个可选参数设为仅关键字,因为我一直在输入gen_stream([...], 9),不小心设置了提取器而不是总数。如果您愿意,您可以保留原始签名或其他变体。


推荐阅读