由itertools.groupby()生成的迭代器意外消耗

时间:2016-01-06 22:24:25

标签: python python-3.x iterator itertools

我编写了一个基于迭代器的小程序来显示多列日历。

在该代码中,我使用master按功能itertools.groupby按月对日期进行分组。在那里,我将月份名称和分组日期作为每个月的列表。但是,当我让该函数直接将分组日期作为迭代器(而不是列表)返回时,程序会将除最后一列之外的所有日期留空。

我无法弄清楚为什么会这样。我使用groupby错了吗?任何人都可以帮我找到消耗迭代器的位置或忽略其输出吗?为什么特别是最后一栏“幸存”?

以下是代码:

group_by_months()

这是失败的输出:

import datetime
from itertools import zip_longest, groupby

def grouper(iterable, n, fillvalue=None):
    """\
    copied from the docs:
    https://docs.python.org/3.4/library/itertools.html#itertools-recipes
    """
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

def generate_dates(start_date, end_date, step=datetime.timedelta(days=1)):
    while start_date < end_date:
        yield start_date
        start_date += step

def group_by_months(seq):
    for k,v in groupby(seq, key=lambda x:x.strftime("%B")):
        yield k, v # Why does it only work when list(v) is yielded here?

def group_by_weeks(seq):
    yield from groupby(seq, key=lambda x:x.strftime("%2U"))

def format_month(month, dates_of_month):
    def format_week(weeknum, dates_of_week):
        def format_day(d):
            return d.strftime("%3e")
        weekdays = {d.weekday(): format_day(d) for d in dates_of_week}
        return "{0} {7} {1} {2} {3} {4} {5} {6}".format(
            weeknum, *[weekdays.get(i, "   ") for i in range(7)])
    yield "{:^30}".format(month)
    weeks = group_by_weeks(dates_of_month)
    yield from map(lambda x:format_week(*x), weeks)

start, end = datetime.date(2016,1,1), datetime.date(2017,1,1)
dates = generate_dates(start, end)
months = group_by_months(dates)
formatted_months = map(lambda x: (format_month(*x)), months)
ncolumns = 3
quarters = grouper(formatted_months, ncolumns)
interleaved = map(lambda x: zip_longest(*x, fillvalue=" "*30), quarters)
formatted = map(lambda x: "\n".join(map("   ".join, x)), interleaved)
list(map(print, formatted))

这是预期的输出:

           January                          February                          March             
                                                                  09           1   2   3   4   5
                                                                  10   6   7   8   9  10  11  12
                                                                  11  13  14  15  16  17  18  19
                                                                  12  20  21  22  23  24  25  26
                                                                  13  27  28  29  30  31        
            April                             May                              June             
                                                                  22               1   2   3   4
                                                                  23   5   6   7   8   9  10  11
                                                                  24  12  13  14  15  16  17  18
                                                                  25  19  20  21  22  23  24  25
                                                                  26  26  27  28  29  30        
             July                            August                         September           
                                                                  35                   1   2   3
                                                                  36   4   5   6   7   8   9  10
                                                                  37  11  12  13  14  15  16  17
                                                                  38  18  19  20  21  22  23  24
                                                                  39  25  26  27  28  29  30    
           October                          November                         December           
                                                                  48                   1   2   3
                                                                  49   4   5   6   7   8   9  10
                                                                  50  11  12  13  14  15  16  17
                                                                  51  18  19  20  21  22  23  24
                                                                  52  25  26  27  28  29  30  31

1 个答案:

答案 0 :(得分:3)

正如文档所述(c.f.):

  

当groupby()对象前进时,前一个组不再可见。因此,如果以后需要该数据,则应将其存储为列表

这意味着当代码稍后无序地访问返回的迭代器时,即当groupby实际被迭代时,消耗了迭代器。由于此处完成的分块和交错,迭代无序发生。

由于我们迭代的方式,我们观察到这种特定的模式(即,只有最后一列完全显示)。那就是:

  1. 打印第一行的月份名称。从而消耗了直到最后一列的月份的迭代器(并且丢弃了它们的内容)。 groupby()对象仅在第一列&#39;之后生成最后一列的月份名称。数据

  2. 我们打印第一周的行。因此,使用传递给zip_longest()的默认值自动填充第一列已经耗尽的迭代器。只有最后一列仍然提供实际数据。

  3. 下一行的月份名称也是如此。