python - itertools.groupby() 生成的迭代器被意外消耗

标签 python python-3.x iterator python-itertools

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

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

我不明白为什么会这样。我使用 groupby 是否错误?谁能帮我找出迭代器被消耗或输出被忽略的地方?为什么最后一栏“幸存”?

代码如下:

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

这是预期的输出:

           January                          February                          March             
00                       1   2   05       1   2   3   4   5   6   09           1   2   3   4   5
01   3   4   5   6   7   8   9   06   7   8   9  10  11  12  13   10   6   7   8   9  10  11  12
02  10  11  12  13  14  15  16   07  14  15  16  17  18  19  20   11  13  14  15  16  17  18  19
03  17  18  19  20  21  22  23   08  21  22  23  24  25  26  27   12  20  21  22  23  24  25  26
04  24  25  26  27  28  29  30   09  28  29                       13  27  28  29  30  31        
05  31                                                                                          
            April                             May                              June             
13                       1   2   18   1   2   3   4   5   6   7   22               1   2   3   4
14   3   4   5   6   7   8   9   19   8   9  10  11  12  13  14   23   5   6   7   8   9  10  11
15  10  11  12  13  14  15  16   20  15  16  17  18  19  20  21   24  12  13  14  15  16  17  18
16  17  18  19  20  21  22  23   21  22  23  24  25  26  27  28   25  19  20  21  22  23  24  25
17  24  25  26  27  28  29  30   22  29  30  31                   26  26  27  28  29  30        
             July                            August                         September           
26                       1   2   31       1   2   3   4   5   6   35                   1   2   3
27   3   4   5   6   7   8   9   32   7   8   9  10  11  12  13   36   4   5   6   7   8   9  10
28  10  11  12  13  14  15  16   33  14  15  16  17  18  19  20   37  11  12  13  14  15  16  17
29  17  18  19  20  21  22  23   34  21  22  23  24  25  26  27   38  18  19  20  21  22  23  24
30  24  25  26  27  28  29  30   35  28  29  30  31               39  25  26  27  28  29  30    
31  31                                                                                          
           October                          November                         December           
39                           1   44           1   2   3   4   5   48                   1   2   3
40   2   3   4   5   6   7   8   45   6   7   8   9  10  11  12   49   4   5   6   7   8   9  10
41   9  10  11  12  13  14  15   46  13  14  15  16  17  18  19   50  11  12  13  14  15  16  17
42  16  17  18  19  20  21  22   47  20  21  22  23  24  25  26   51  18  19  20  21  22  23  24
43  23  24  25  26  27  28  29   48  27  28  29  30               52  25  26  27  28  29  30  31

最佳答案

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

when the groupby() object is advanced, the previous group is no longer visible. So, if that data is needed later, it should be stored as a list

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

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

  1. 打印第一行的月份名称。因此,直到最后一列月份的迭代器都被消耗(并且它们的内容被丢弃)。 groupby() 对象仅在第一列数据之后生成最后一列的月份名称。

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

  3. 月份名称的下一行也会发生同样的情况。

关于python - itertools.groupby() 生成的迭代器被意外消耗,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34644010/

相关文章:

python - 连接两个列表的笛卡尔积的字符串(最好没有 for 循环)

python - 我怎样才能加快这个 django orm 生成的查询?

python - 根据大小对组进行排名

python - 为什么 "5 in range(5,6) in [range(5,6)]"返回 True?

c++ - 在 C++ 中删除映射迭代器的行为不一致

Java foreach NoSuchElementException

python - 检查字符串是否只是字母和空格 - Python

python - Windows 中的 py 启动器 : Selecting a specific python installation(all of them same version of 3. 7-64)

python - lxml.etree.ElementTree 破坏变音符号

python - 使用正则表达式从特定文本格式获取信息