2016-01-06 41 views
2

我已經編寫了一個基於迭代器的小程序來顯示多列日曆。itertools.groupby()生成的迭代器意外消耗

在該代碼中,我使用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 
+0

您是否閱讀過[文檔](https://docs.python.org/2/library/itertools.html#itertools.groupby)? – BrenBarn

+2

@BrenBarn顯然不夠徹底。你是說這個部分? _「當groupby()對象被提前時,先前的組不再可見,因此,如果稍後需要該數據,則應將其存儲爲列表」_ – moooeeeep

+2

Right,該部分。 – BrenBarn

回答

3

由於文檔狀態(c.f.):

當GROUPBY()對象被前進時,前一組是不再可見。所以,如果以後需要這些數據,它應該被存儲爲列表

這意味着迭代器被消耗,當後面的代碼訪問返回的迭代器出故障,即當GROUPBY實際上是重複。由於在此完成的分塊和交織,迭代發生無序。

由於我們迭代的方式,我們觀察到這種特定的模式(即只有最後一列完全顯示)。即:

  1. 將打印第一行的月份名稱。因此直到最後一列的月份的迭代器都被消耗掉了(並且丟棄了它們的內容)。 groupby()對象只在第一列的數據後面生成最後一列的月份名稱。

  2. 我們打印第一週的一行。因此對於第一列已經用盡迭代器填補了使用自動傳遞到zip_longest()默認值。只有最後一列仍然提供實際數據。

  3. 對於下一行的月份名稱也是如此。

+0

當您在groupby()'對象上調用'next()'(顯式或隱式地,例如,通過'for'-loop)時,迭代器被消耗(如果它們還沒有耗盡)。當代碼稍後訪問返回的迭代器時,它們已經耗盡。 – jfs