2008年8月21日木曜日

Python である月の日曜日の日付のリストを取得

指定した年月における「特定の曜日」の日付を取得したい。例えば、

「 2008 / 8 において日曜日である日付のリストを得たい」

とする。

5.18 calendar -- 一般的なカレンダーに関する関数群 によると、

monthcalendar(year, month)

月のカレンダーを行列で返します。各行が週を表し、月の範囲外の日は0になり ます。 それぞれの週はsetfirstweekday()で設定をしていない限り月曜日か ら始まります。

上記の関数とリスト内包表記を用いて、

import calendar

# 2008/8 の日曜日の日付をリストとして取得
print [x[calendar.SUNDAY] for x in calendar.monthcalendar(2008, 8)]

結果は次のように表示される。

[3, 10, 17, 24, 31]

 

monthcalendar の定義

\Python25\Lib\calendar.py を見ると、上記のようなメソッドは Calendar クラスに定義されておらず、代わりに

# Support for old module level interface
c = TextCalendar()
#...
monthcalendar = c.monthdayscalendar

というように変数に関数が代入されている。 TextCalendar クラスは Calendar クラスのサブクラスで、 monthdayscalendar() 関数は Calendar クラス内で定義されている。そして同ファイルの変数 __all__ にシーケンスの要素として、 文字列 “monthcalendar” が存在する。上記で引用したドキュメントはリリース 2.4 に基いており、今自分が使っている Python のバージョンは 2.5.2 。 5.2 calendar -- General calendar-related functions を見ると Python 2.5 でいろいろと関数が追加されているのがわかる。

ところで上記の __all__ っていったい何だろうか? (@_@;)

 

public names

6.12 import 文 によると、

モジュールで 公開されている名前 (public names) は、 モジュールの名前空間内にある __all__ という名前の変数を調べて決定します...

__all__ 内にある名前は、全て公開された名前であり、実在するものとみなされます。

(太文字は引用者による)

つまり、モジュールのインターフェイスのようなものか。逆にこれが定義されていないと、

__all__ が定義されていない場合、モジュールの名前空間に見つかった名前で、アンダースコア文字 ("_") で始まっていない全ての名前が公開された名前になります。

(同上より、太文字は引用者による)

Python って、クラスのコンストラクタ __init__(self) のように、アンダースコアではじまると特別な動作がされるわけか。 (@_@)

結局、この __all__ の役割というのは、

__all__ には、公開されている API 全てを入れなければなりません。 __all__ には、(...) API を構成しない要素を意に反して公開してしまうのを避けるという意図があります。

(同上より、太文字は引用者による)

というように、情報隠蔽するものであって、疎結合を保つための手段の一つのようだ。

 

monthdayscalendar 関数

上記の定義より、monthcalendar 関数は別の関数へのアダプターになっているようだ。5.2 calendar -- General calendar-related functions によると、

monthdayscalendar(year, month)

Return a list of the weeks in the month month of the year as full weeks. Weeks are lists of seven day numbers.

この実装を見ると、

days = list(self.itermonthdays(year, month))
return [ days[i:i+7] for i in xrange(0, len(days), 7) ]

イテレータを返す関数があり、それを元にして上記の関数が定義されている。

itermonthdays(year, month)

Return an iterator for the month month in the year year similar to itermonthdates(). Days returned will simply be day numbers.

Calendar モジュールでは、日付を数字で返す関数と、datatime オブジェクトを返すものがあるようで、monthdayscalendar  に対応して、よく似た名前の monthdatescalendar がある。はじめこの違いに気がつかずに、こちらを使って monthcalendar と同じようになるように書いていた。

print [x[calendar.SUNDAY].day for x in cal.monthdatescalendar(2008,8)]
datetime オブジェクトには day という日付にアクセスするための属性があったのでそれを利用した。

 

その他

さて、変数 __all__ も出てきたことだし、 6. モジュール の構成方法についてもそろそろ目を通さねば…(@_@)