みろりHP


緑色さんの多目的ブログ
みろりえいちぴー
ごゆるりとおくつろぎあさーせ。
| カテゴリ:プログラミング |
Python InputGhost1.1 コピペ作業自動化ツール



みろりHPのファイル置き場 - InputGhost1.1

知り合いが「膨大なテキストを一行ずつコピペする」というスゲーしち面倒な作業をしてんだよ。ペースト先のアプリケーションの仕様で、まとめて全部貼り付けることができないんだ。貼り付ける先がテキストエリアじゃなくて改行不可のテキストボックスだから。だからそいつは、
  • 一行コピーする。
  • 対象アプリ開いてテキストボックスにペーストする。
  • またテキストへ戻って、一行コピーする。
  • 対象アプリ開いて次のテキストボックスにペーストする。
  • 以下ずっと繰り返し。
なんつーことをしてた。ねーよ。と思った。だからその作業の自動化ツールを作ってやったわけさ。



一行ずつコピペしたいテキストをこんなふうに用意しとく。


これ設定ファイル。一番下に、自動化する行動の順番を書く。今回は「改行して、一行コピペして、改行する」ってのをワンループとする。


実行すると、指定したアプリケーションが前面に出てきて、設定した行動を自動で行ってくれる。今回の場合、メモ帳が出てきて、改行コピペ改行が自動で行われる。


今回はユーザを上述の知り合いに限定してるから汎用的なツールではない。その知り合いに必要な入力が「改行、コピペ、『0』を入力」だけだったから、それ以外の行動は登録できない。あと、元テキストから空白を除いたり、空き行は無視するようになってる。だけどプログラムを使って入力を再現できるノウハウが手に入ってよかったぜ。ネトゲのBOTとかもこういうのが元になってたのかな。すくなくとも連打ゲー用のチートツールくらいなら、もう作れそうだ。



ソースは冒頭の公開セットに同梱してあるんで、今回新出だったtipsを書いとく。

対象のアプリケーションを引っ張り出すには?
subprocess.Popen('call notepad.exe', shell=True)

こう。subprocessはビルトインモジュールなので簡単だね。んー、もしメモ帳が複数起動していて、そのうちのひとつだけを対象にしたい場合はこれじゃあダメだよね。複数のウィンドウのうちひとつを選択して引っ張り出さないといけなさそうだが……うん、そのへんは機会があったらトライかな。こないことを祈る。メチャ面倒そうな予感がする。

キー操作の代行
user32 = ctypes.windll.user32
user32.keybd_event(ここにキーコード, 0, 0, 0)   # 押す
user32.keybd_event(ここにキーコード, 0, 0x2, 0) # 離す

今回の主役。えーとこれはちょっと調べた感じ、「ビルトインモジュールctypesが、windllっていうWindowsのapiを呼び出して、その中のuser32っていうapiを使わせてもらう」って流れみたいだ。キーコードは十進数で表記(参考サイト:仮想キーコード一覧)。

コピペを代行
pyperclip.copy('対象テキスト')
time.sleep(0.1)
user32.keybd_event(cls._KEYCODE_CTRL, 0, 0, 0)
user32.keybd_event(cls._KEYCODE_V, 0, 0, 0)
user32.keybd_event(cls._KEYCODE_CTRL, 0, 0x2, 0)
user32.keybd_event(cls._KEYCODE_V, 0, 0x2, 0)

今回コピペしたいテキストはプログラム側がもってるので、動作としては、「テキストをクリップボードに貼り付けて」「Ctrl+Vを代行させてペーストする」って方式をとってみた。クリップボード操作のpyperclipはビルトインではないからpipでインストールしないとダメ。クリップボード操作の直後の「0.1秒待機」が意外に超重要で、これを挟まないとコピペのとき想定通りの動きをしないことがある。クリップボードへの貼り付けが終わる前にCtrl+Vを押しちゃったりするのが原因かなあ? たぶん。


| 緑色 | プログラミング | comments(0) |
| カテゴリ:プログラミング |
Python クラスって何なの



またクラスの話かよ。思えば、pythonを始めて、ちょこちょこ手遊び2年が経過した。記念に2年前の俺へクラスの解説を書いてみる。あの頃はとりあえずドットインストールのpython講座動画で入門したのだよな。振り返ってみようとドットインストールサイトに行ってみたら、python講座が有料になっていた。残念。



さてきみは、足し算の書き方や関数の書き方、リストやディクショナリは理解できたのに、このクラスとかいう謎の何かへ単元が移った途端突然理解不能になり困惑しているところだと思う。ぶっちゃけ直近スクリプト書くにあたっては必要ないと思うし、別にいっかーと思いつつも、ネットに載ってるサンプルスクリプトではやたらめったらクラスが使われているのでますます困惑を極めていることだろう。そのふたつの困惑をとりあえず解決してやるぜ。

クラスってのはディクショナリみたいなもんだから安心しろ
少なくともきみの2年後である俺は、まあ基本的にはディクショナリみたいなもんだと思いながら使っている。下の例を見てみれば、ディクショナリを定義して値を取り出すのも、クラスを定義して値を取り出すのも似たようなものだとわかるだろう?
# ディクショナリ
dic = {
    'a': 'A',
    'b': 'B',
    'c': 'C',
}
print(dic['a'])  # 'A'が取り出せる。

# クラス
class Klass:
    a = 'A'
    b = 'B'
    c = 'C'
print(Klass.a)  # 'A'が取り出せる。
うん? 似てるっぽいけれど、ディクショナリではキーを文字列('a')で指定しているのに対し、クラスのほうは変数(a)で指定しているから違うんじゃないかって? ならばこう書いてやろう。
Klass_dic = Klass.__dict__
print(Klass_dic['a'])  # 'A'が取り出せる。
__dict__
はクラスの中身をディクショナリ形式にしてくれる。これでクラスっていう何かがぶっちゃけディクショナリみたいなもんだということがワカると思う。

サンプルスクリプトでやたらクラスが使われてんのはそれが便利だからだ
きみの2年後である俺がスクリプトにクラスを使うのは、パッケージ化がしたいときだ。今のきみでも、スクリプトを適当に書くより関数にまとめたほうが良いカモっていう感覚はなんとなくもっているだろう? 使い回しがきくようになるし、
変数のスコープが深くなるから他のトコで使う変数と名前衝突するのを防げるもんね。あとまとまってる感じがなんか嬉しいだろ? クラスを使うのはそれと同じ理由だ。きみがテキトーな小さな計算スクリプトをさっくりまとめたくて関数を作るのと同じテンションで、俺はすこし大規模な計算や複数の関数をさっくりまとめたくてクラスを作るのだ。クラスを使うと、{クラス名}.{関数名}って感じで名指しで内側の関数を実行できたり、{クラス名}.{変数名}って感じで値を取り出せたりできる。後者は上述したようディクショナリでも簡単にできるけれど、ディクショナリの中に関数を定義するのはちょっと面倒だし見栄えも気になってくる。
# ディクショナリの中で関数定義する場合
dic = {
    'a': 'A',
    'd': lambda s: s + dic['a']
}
print(dic['d']('a'))  # 'aA'って表示される

# クラスの中で関数定義する場合
class Klass:
    a = 'A'
    def d(s):
        return s + Klass.a
print(Klass.d('a'))  # 'aA'って表示される
後者のほうが書きやすく見やすい感じするだろう。きみの書いているスクリプトが全部ひとつのディクショナリにつっこめる仕組みがクラスってことだ。



幕間。
きみはこれから解説記事で「クラスが設計図でインスタンスがロボットやクルマ」系の例え話をよく目にすることになると思うが、それはきみの役には立たないので見なくてよい。きみはヘンに例え話をされるより、実際の言語仕様で説明されたほうが頭に入るヤツだ。



これだけ執拗に言われればクラスがディクショナリみたいなもんだということは十分わかったと思うけれど、インスタンスって何なのっていう疑問も連なってくるだろう。

インスタンスもディクショナリだと思ってイイよ
いやお前なんでもディクショナリかよときみはツッコミを入れてくるかと思うが、2年後の俺がそういう理解でクラス・インスタンスを実際不自由なく書けるのだから大丈夫だ。てかpythonスクリプトなんてすべてがディクショナリみたいなもんなのだ。数ヶ月後きみは試行錯誤する中でその片鱗に気がつくことになる(Python 変数もどき自動作成)し、さらにその半年後、オライリーという教典を読んでリファレンスという概念を知りその理解が正しかったことを知ることになる(Mark Lutz『初めてのPython』)。a,b,cというクラス変数をもったクラスと、a,b,cというキーをもったディクショナリがおんなじようなもんであるように、a,b,cというクラス変数をもったインスタンスもおんなじようなもんだ。
class Klass:
    a = 'A'
    b = 'B'
    c = 'C'

klass = Klass()
print(klass.a)  # 'A'って表示される
少なくともこの例だとインスタンスがインスタンスである意味がゼロだが、「クラスって何なの」と言ってる段階ではここまででよいと確信してる。その段階でインスタンスのステキな使い方をいっぺんに詰め込まれるとホント厳しくなる。とにかくディクショナリだ。ディクショナリでイイ。


| 緑色 | プログラミング | comments(0) |
| カテゴリ:プログラミング |
Python 波ダッシュ問題レポート



「1日1python」の時間です(ただし隔週放送)。みろりhpの記事をpythonで読み込んでいたら、スゲーイヤな問題に咬まれたのでそれについて書く。
なお今回のレポート内容はこのブログだとなんか手に余る感じ。ブログの文字コードがEUC-JPだからだ。今回の内容はUTF-8で書きたいところ。jugemブログは早急に最先端であるUTF-8を導入して頂きたいものだ。これについては随時補足しとく。



マジびっくりなことに、
は違う文字だ。前者は「波ダッシュ(WAVE DASH)」といって、後者が「全角チルダ(FULLWIDTH TILDE)」という。
pythonで下記スクリプトを実行してみればそれがわかる。
wavedash = '〜'        # 前者。波ダッシュ。
fullwidthtilde = '〜'  # 後者。全角チルダ。
print(wavedash == fullwidthtilde)  # Falseになりやがる。ビックリだな。
UTF-8にはどっちの文字も含まれてるんだが、EUC-JPとかSJISには全角チルダのほうが含まれていないらしい。だからこのレポートはUTF-8で書きたかったわけ。
【補足】一応上のスクリプトではエスケープ? がかかってちゃんと二種類表現されているのでコピペしてpython実行してもFalseは得られるみたい。

見分け方はこう。
# b'\xe3\x80\x9c' って出れば波ダッシュ。
print(str(wavedash.encode('utf8')))

# b'\xef\xbd\x9e' って出れば全角チルダ。
print(str(fullwidthtilde.encode('utf8')))

上の見分け方を使って判別関数を作っといたぜ。
def is_WAVEDASH(char):
    return char.encode('utf8') == b'\xe3\x80\x9c'

print(is_WAVEDASH(wavedash))        # Trueになる。
print(is_WAVEDASH(fullwidthtilde))  # Falseになる。


def is_FULLWIDTHTILDE(char):
    return char.encode('utf8') == b'\xef\xbd\x9e'

print(is_FULLWIDTHTILDE(wavedash))        # Falseになる。
print(is_FULLWIDTHTILDE(fullwidthtilde))  # Trueになる。

変換する関数も作ったぜ。てか今回の問題で俺がいちばん必要としたのがこれ。波ダッシュと全角チルダが混在するデータで処理をしようとするとどっかでオカシクなる。文字化けしたり、見た目には同じ文字なのに「違う」って言われたり(冒頭のヤツ)ね。そういう恐れのある文字列は全部以下の関数を通しちまおうぜ。
# 波ダッシュを全角チルダに変換する。reverseにTrue渡せば逆になるよ。
def convert_WAVEDASH_to_FULLWIDTHTILDE(string, reverse=False):
    wavedash = (b'\xe3\x80\x9c').decode('utf-8')
    fullwidthtilde = (b'\xef\xbd\x9e').decode('utf-8')
    if not reverse:
        return string.replace(wavedash, fullwidthtilde)
    else:
        return string.replace(fullwidthtilde, wavedash)



この問題に出会ったのは、みろりhpのデータをxmlエクスポートしたときだ。ナゼかこのブログはEUC-JP(波ダッシュしかないほう)で書かれているのに、出力するとUTF-8(どっちもあるほう)になり、しかももともと含まれていた波ダッシュがすべて全角チルダに置き換わる謎仕様だったんだ。それを処理しようとしたらおかしなことになった。そこでこの調査が必要になったわけ。


| 緑色 | プログラミング | comments(0) |
| カテゴリ:プログラミング |
Python raiseノート



「1日1python」の時間です(ただし隔週放送)。ようやく raise を理解したのでまとめるぜ。raise文との出会いはオライリーだった。raiseとは例外を飛ばすものとの事だったけど、そもそも例外って何かわかんねーしassertっていう何か似てるのもあるしで全く理解できなかったのだよな。でも先日プログラマさんの書いたコラムとか読んでて、throwっていう他の言語の構文を知ったことでとうとう理解できた。



例外って何?
プログラム実行中に起こる、想定外の現象のこと。ほんでプログラム実行中に起こる想定外っつったらエラーのことだから、まあエラーのことだ。以下のように書き下したらよく分かったよ。
  • 例外 -> 想定外 -> プログラムにおける想定外とは? -> エラー
まあその、ドシロートっていうのはこういう用語ひとつでも引っかかっちゃうものなのですよ。pyてょんちゃんが「Exception!!」って叫んだら、「作者のお前が想定してなかったことが起きたぜ!」って意味になるわけだね。

raiseって何?
raiseは例外を生み出してひとつ上の階層に投げる呪文だ。いやエラーなんて放っておいても起こるんだから…なんでわざわざ自分で作るわけ!? ってのがオライリー読んだときは疑問で理解ができなかったんだけど、python的にはエラーじゃないけど俺的にはエラーになって欲しいって状況を想定したら理解できた。
def foo(flag):
    if not flag:
        raise Exception('Falseが来たから俺の都合で終了するよ。')
    return flag

foo(False)
実行すると以下のようになる。
Traceback (most recent call last):
  File "/Users/.../py.py", line 11, in <module>
    foo(False)
  File "/Users/.../py.py", line 8, in foo
    raise Exception('Falseが来たから俺の都合で終了するよ。')
Exception: Falseが来たから俺の都合で終了するよ。
いやー、Exceptionてのは俺の手中にはない範疇のモノだと思っていたから、ちょっと感動ですね。

感動ついでに自作のExceptionを作ってみる
こんなの書いたらできたわ。
class MidoriException(Exception):
    def __init__(self, error_message='緑エラーだよ。'):
        self.error_message = error_message
    def __str__(self):
        return self.error_message

raise MidoriException()
実行すると以下。
Traceback (most recent call last):
  File "/Users/.../py.py", line 11, in <module>
    raise MidoriException()
__main__.MidoriException: 緑エラーだよ。
すげえMidoriExceptionなんつーのが表示されちゃったぜ。なにこれ面白いんだけど。python的にはエラーじゃないんだけど俺の都合でエラーにする箇所については俺オリジナルのExceptionクラスを使えば、結果を見たときわかりやすいかもしんない。

「上の階層に投げる」てのはどゆこと?
階層ってのは「関数の呼出元」と「呼ばれた関数」の層のことだ。「呼ばれた関数」側でraiseすると、生み出したExceptionは「呼出元」へ浮かび上がる。俺が浮かび上げるから raise なんだな。他の言語では throw っていう文が使われてるらしいけど、そっちでは「浮かび上げる」じゃなくて「投げる」っていう感覚なんだね。 ちょっと書いてみる。グローバル -> foo -> bar -> baz の順番で階層的に関数を呼び出して、第四階層であるbazでMidoriExceptionをraiseしてみるぜ。
# 第四階層
def baz():
    # 上の階層barにExceptionを能動的に浮かび上げ(raise)る。
    raise MidoriException('第四階層でエラーが発生しました。')

# 第三階層
def bar():
    # 浮かび上がってきたExceptionは勝手に上の階層fooに浮かび上がる。
    baz()

# 第二階層
def foo():
    # 同じく、勝手に上の階層に浮かび上がる。
    bar()

foo()
なお、fooの中でtryを書いてbarから浮かんできたExceptionを捕まえて、自分で改めてraiseしても同じ結果になるみたい。
def foo2():
    try:
        # ここで捕まえる。
        bar()
    except Exception as e:
        # 捕まえたのを浮かび上げる。
        raise e
てことはさ、普段エラーが起こっているときはその場で勝手にraiseが起きていると思ってよさげ。あーなるほどね!



「例外」が理解できなかったことについては上述した。いっぽう、raiseが理解できなかった理由は、raiseという言葉が指すものを初見で勘違いしたからだ。つまり、実際は「例外を生んで上の階層に浮かび上げる」ことを指すんだが、俺は「例外を生むこと」自体をraiseだと思っちゃったんだよ。こう、俺にはよくわからないpythonの内部から、Exceptionというものを持ち上げるっていうイメージ。それは間違ってた。「Exceptionというものを持ち上げる」のは
Exception()
だ。raiseをつけることで初めてそれが上層へ飛び上がる。で、まあそういう勘違いをしてたんだけど、他の言語のthrowっていう言葉だとそういう間違いをしようがないよな。しかも他の言語だと
throw new Exception();
っていう書き方をしてたんだよ。「新しくExceptionてクラスのインスタンス作ってそれを投げるのね」ってスグわかった。で、pythonだとそういう構文はあるのかな? って考えて、ああ、raiseってこのことだったのか! と思い至れたってわけ。


| 緑色 | プログラミング | comments(0) |
| カテゴリ:プログラミング |
Python pygameをMacで使うのはヤメとこうぜ



いやな? 俺が心血を注いで組んだpygameアプリがMacでは動かなかったのだよ。特に咬まれた以下二点について書く。
  • mp3を読み込んでくれねー( pygame.error: Unrecognized music format )
  • キーボード入力を受け付けてくれねー



mp3を読み込んでくれねー
oggに変換しようぜ。俺にはよくわかんないんだけれど、なんでも SDL_mixer(?) だの SMPEG(?) だのをbrewすれば使えるよ? みたいな情報があったんだけど、うまくいかなかったよ。だからoggに変換しようぜ。ひとまずMacフリーソフトで探したところ Video Converter Master Lite というアプリがよい感じだった。ちょっとmp3->oggのボタンが探しづらかったけれどちゃんとあるぜ。

キーボード入力を受け付けてくれねー
諦めようぜ。とりあえず状況から説明するとだな……、Mac上でpygameウィンドウのアプリを起動すると、そのウィンドウアプリが独立した新しいアプリケーションとして認められないみたいなんだよ。すると、そのpygameアプリが独立してキーボード入力を受け取ってくれない。Sublime Textで起動すればSublime上でキー入力したことになっちゃうし、ターミナルで起動すればターミナルでキー入力したことになっちゃう。ただしマウスイベントは受け付けてくれる。これはわからん諦めようぜ。Windows上では依然として動くので、それでお茶を濁す感じでひとつ。ゆえに「DialogFrame」らへんの自作アプリはWindows専用ってことになる。



本題は以上なんだけど。「これまでのゲームってMacでも動くのかな調査」にともなって自作ゲームであるところの「一石二鳥」をちょいと普通にプレイして自己レビューしてみた。なお「一石二鳥」は上述したような厄介な問題はなしできちんと動いたぜ。細かいことを言えばWindowsとMacのデリミタ違いを直す必要があったケド。
  • クリアはできた。
    • <SYSTEM>クリアおめでとうございます。あなたのスコアは 9670 でした。
  • ちとスタートが切りづらいなコレは。弾薬とお金は最初からもっとあっていい。
  • ベースキャンプでの弾薬補給は1個じゃなくて5個くらいでもいいんじゃね。商人にたどり着くまでは補給と石拾いを繰り返すことになるが、それはテンポが悪い。
  • 害獣との戦闘後は石ドロップがあってもいいと思う。ご褒美ナシだと戦闘が面倒なだけだ。
  • コレクションブックの点数最大値とか、トロフィーの取得条件は可視でもいいと思う。

典型的な「素人の作るゲームは難易度が高くなりがち説」に引っかかってしまったようだな。そう考えてみるとマゾゲーってのはすげーよな。難易度を上げつつ楽しめるようにデザインされてんだから。ギターといい歌といい絵といいゲームといい、自分で一度やってみると先行物のスゴさがわかっちまうよなあ。


| 緑色 | プログラミング | comments(0) |
| カテゴリ:プログラミング |
Python デコレータ奮闘記つづき



目次。


こないだのデコレータ奮闘記はおおむね無事閉幕したのだけれど、ひとつ疑問が残っていた。デコレータというロジックが登場する場面には汎用関数先行型とデコレータ先行型があるが(ネーミングは俺)、なんで後者にだけ@の省略記法があるんだろう? ってものだ(先回の一番最後に書いたやつ)。この件に自分なりの解決がつけられたので書く。

みどりんが呼ぶところの汎用関数先行型 みどりんが呼ぶところのデコレータ先行型
def call():
    # デコるプログラム

def deco(func):
    # call()をデコるデコレータ

call = deco(call)
def deco(func):
    # デコレータ

# 省略記法
@deco
def call():
    # デコるプログラム

それはだな…
  • デコレータが登場する場として二通りあるってのは間違いないと思うんだけど、それぞれ、デコレータを作る人間の立場が違うんだ。
  • 汎用関数先行型ではデコレータを作る人間はデコレータの「製作者」。
  • デコレータ先行型ではデコレータを作る人間はデコレータの「利用者」。
  • 省略記法ってのは、利用者が便利になるように存在するものだ。だから「利用者」を対象とした場面にしか@の省略記法が存在しないんだ。
  • てかそもそも汎用関数先行型って、デコレータがデコレータである意味が薄いだろう。上のスクリプト例でいえば、デコレータが「call()をデコるデコレータ」ってなってるけど、それはただのcall()の拡張にすぎないだろう? クリスマスツリーのデコレーションってあるけど、あれはクリスマスツリーにしか使えないんだからもはやクリスマスツリーの一部だろう? いや例示に失敗してる感が否めないがそういうことだと思うんだ。特定の関数だけを装飾するためのデコレータはその関数の拡張に過ぎない。デコレータじゃない。だったらデコレータの省略記法も存在しないよそりゃ。
  • 一方、デレコータのセットが用意されてて、みなさんの好きな関数をデコレーションして楽しんでねーって提供されてたら、それは完璧にデコレータだ。プレゼントの包装紙は何にでも使える。何でもラップできる。だから利用者に向けてキレイな包み方とか簡単な包み方もまた提供されてる。それがデコレータの省略記法だ。



ところで、だとすれば、デコレータは汎用性に富んだ書かれ方をすべきだろう。俺が先回から書いてたやつはまさにcall()の拡張にすぎなかったから、めっちゃ抽象度の低い作りだったよな。デコレータ自体に引数を取らせたり、可変長引数に対応したデコレータの書き方もついでに覚えておくぜ。

例として、関数をデコって返り値の文字数を数えるデコレータを作ってみる。ほんで、デコレータ自体にTrueを与えたらスペースも数えて、Falseを与えたらスペースはカウントしない、って作りにする。なんかねえ、デコレータ自体に引数を渡すには、デコレータをさらにネストして定義しないといけないらしいぜ。
def deco_wrapper(count_space):
    def deco_count(func):
        def func_kari(*args):
            if count_space:
                return len(func(*args))
            else:
                return len(func(*args).replace(' ', ''))
        return func_kari
    return deco_count
可変長引数の対応は、単にネストされたfunc_kariの引数部分を全部*argsにするだけだぜ。使い方が以下。
@deco_wrapper(True)
def call1(name):
    return name

@deco_wrapper(False)
def call2(name1, name2):
    return name1 + name2

# count_spaceをTrueにしたので結果は5
print(call1('x x x'))

# count_spaceをFalseにしたので結果は6
print(call2('x x x', 'y y y'))
オッケー、これはそこそこ汎用性がありそうだぜ。ってアレ? こういうデコレータ定義の仕方だと、@省略記法を使わない書き方には対応できねーんじゃねーか?



疑念は的中し、@記法を使わず、デコレータに引数を渡したい場合は以下のようなデコレータ定義をしねーといけない。
def deco_count(func, count_space):
    def func_kari(*args):
        if count_space:
            return len(func(*args))
        else:
            return len(func(*args).replace(' ', ''))
    return func_kari
使うときはこう。
def call3(name):
    return name

def call4(name1, name2):
    return name1 + name2

# @記法はこのへんを簡略化してる。
call3 = deco_count(call3, True)
call4 = deco_count(call4, False)

# count_spaceをTrueにしたので結果は5
print(call3('x x x'))
# count_spaceをFalseにしたので結果は6
print(call4('x x x', 'y y y'))
あ、こっちはデコレータがさらにネストされることもないし、定義したとおり、見たまんまの動きになるから好みかもしらん。ってアレ? こっちはこっちで@記法に対応できねーんじゃねーか?

@deco_count(True)
def call5(name):
    return name
これはエラーになる。



いやつまり、これはつまり、@使用のデコレータと@不使用のデコレータはてんで別物ってことになるよな!? 何が「省略記法」だ。省略じゃないよコレ。まるで別物だよ。つーわけで今回の奮闘の結論は以下。
  • 俺が「汎用関数先行型」と呼んでたデコレータはデコレータじゃなくてただの関数拡張
  • 俺が「デコレータ先行型」と呼んでたものが正当なデコレータ(何が先行型だ。デコレータは先行するものなのだ)
  • 俺が「@省略記法」って呼んでたのは大間違い。まったく省略じゃない
  • デコレータの書き方には「@記法」と「見たまんま記法」があるが、それぞれデコレータの定義が別物
  • デコレータを作るときはどっちの記法で使わせるか先に決める必要がある

おつかれさまでしたァ!


| 緑色 | プログラミング | comments(0) |
| カテゴリ:プログラミング |
Python python3でウェブサーバを作る(Bottleでリトライ)



一年前、pythonでウェブサーバを作ろうとしたことがあったんだけれど、道半ばで挫折した。確かcgiとかいう機能を使ったり、pyスクリプトとテンプレートを分けたり、中途半端に非同期処理を組み込んだりした挙句、日本語表示がうまくいかなくてイヤになってヤメちゃったんだよね。そのリトライをするぞ。あれから一年、趣味とはいえきちんとクラスを覚え、ちっちゃなものではあるがゲームを作り、Skype Botを作り、DialogFrameなんつー俺俺フレームワークまで作ったのだ。今ならばいける! かもしれない!
というわけで「1日1python」のお時間です。今回は前回のhttp.serverではなく、Bottleモジュールを使ってウェブサーバを作る。
先回のhttp.serverをもう一度触り直してみたのだけれど、「pythonを使ってページを表示するにはどうしてもURLが/cgi-bin/になっちまう。もっと自由にURLを決めたい」「htmlテンプレートの中でpython側の変数とか処理をかけない」なんつー不満点があったのよな。でもBottleがすべて解決してくれた。



目次。
0. とりあえずこれ書いとく
#!/usr/bin/env python
# coding: utf-8
from bottle import route, run, template, request
from bottle import error, redirect, HTTPResponse, static_file

# これは一番下に
run(host='localhost', port=8000, debug=True, reloader=True)
以降のスクリプトは全部この中に書く。なお localhost を 0.0.0.0 にすると、サーバが外からも見えるようになるようだぜ。俺はそうやって、スマホからチェックをしていた。

1. 一番シンプルなやつ
@route('/1/')
def page_01(name):
    return '<h1>YO!</h1>'
http://localhost:8000/1/ でアクセスできる。

2. URLにワイルドカードを使う
@route('/2/:name')
def page_02(name):
    return f'<h1>YO {name}!</h1>'
http://localhost:8000/2/Wada とかでアクセスできる。

3. テンプレートを使う
python側がこう。テンプレート側へ変数を渡すにはこういう書き方をする。
@route('/3/')
def page_03():
    a = 'LALALA'
    b = {'0':'AAA', '1':'BBB'}
    return template('template_03', a=a, b=b)
テンプレートはpythonスクリプトと同階層に
views
というディレクトリを作ってその中に置く。以下にpythonから受け取った変数の表示方法とかテンプレート内pythonスクリプトの書き方とかを記載する。
# template_03.tpl

■ pythonスクリプトから受け取った変数の表示
ふつーの変数: {{a}}
ディクショナリとか: {{b['0']}}

■ for文
<ul>
% for i in range(5)
    <li>{{i}}</li>
% end
</ul>

■ if文
% if b['1'] == 'BBB':
    <p>...</p>
% else:
    <p>...</p>
% end

■ いちいち % を行頭につけるのが面倒なアナタには
<%
    num = len(b)
    if num in [1]:
        pass
    elif num in [2]:
        pass
    end
%>

4. GETクエリを使う
@route('/4/')
def page_04():
    # get一覧を見たいならこれ
    print(request.query.__dict__)

    # クエリの取り出し方
    get_content = request.query.a

    return template('template_04', get_content=get_content)
テンプレート側は普通のgetフォームなので割愛。

5. POSTクエリを使う
@route('/5/')
@route('/5/', method='POST')
def page_05():
    # post一覧を見たいならこれ。
    print(request.params.__dict__)

    # 取り出し方。
    post_content = request.forms.get('name')

    return template('template_05', post_content=post_content)
テンプレート側は普通のpostフォームなので割愛。POSTのほうは、route()デコレータ表記をふたつ書かないといけないのに注意。

6. リダイレクトする
@route('/6/')
def page_06():
    return redirect('/5/')

7. ステータスコードでハンドリングする
えっとぶっちゃけ俺はステータスコードなるものは404くらいしかしらんので404の例を。存在しないURLにアクセスしたら、/5/にリダイレクトするようにしてみた。
@error(404)
def error_404(error):
    r = HTTPResponse(status=302)
    r.set_header('Location', '/5/')
    return r
errorを使った場合、6で使用したredirect()は使えないみたい。でも上述の書き方をしたらむりくりリダイレクトできた。HTTPヘッダにステータスコード302(リダイレクト)と飛び先を設定する方式。

8. 静的ファイルを使う
pyファイルと同階層に静的ファイルを置くディレクトリ(例では
static
)を作り、ソコを静的ファイル置き場として登録する。
@route('/static/:file_path')
def static(file_path):
    return static_file(file_path, root='./static')
HTMLから呼び出すときはこんな感じの相対パスで。
<img src="/static/image/python3.png">



こんなところで俺のリトライは無事成功した。自身の成長を感じられてふんすふんすしつつ改めて先回の奮闘を見ると、いやあpython全然関係ないところで詰まってんなあ。「ネットに情報がねえ」なんて理由で挫折しているが、まあ多分それは、当時もBottleとかDjangoとかそういう単語自体は目に入ってたんだろうけど、馴染みのないモジュールの蓋を開けてみることにまだ抵抗があったのだろう。ちなみにBottleの前にDjangoも試してみたが、あっちはなんかスゲー規模がでかいわディレクトリに色々ファイルが増えやがるわでタイヘンだったので、import一本で済むBottleを今回は採用した。
使用をヤメたhttp.serverについては、使うのがhtmlだけだったら即採用だと思うぜ。なにせターミナルから一行で立ち上がるからな。
$ python -m http.server ポート番号
学習の集大成として、小さなジャンケンゲームを作ったので下に載っけとく。スクリプトと、htmlを載せたところで、あっそうかhtmlを載せるってことはcssも載せないと締まらないのか…クソ長くなっちゃう…ヤだ…ってなったので折りたたんどく。




こんなゲーム。

続きを読む >>
| 緑色 | プログラミング | comments(0) |
| カテゴリ:プログラミング |
Python デコレータ奮闘記



見たことない構文が目に留まった。
@property
def foo(self):
    なんちゃらかんちゃら
なんだこの@は? 調べたところによるとデコレータっていうものらしい。ひとつ覚えてみるかというわけで「1日1python」のお時間です。



どうもデコレータは機能ってよりプログラムの書き方の技術のひとつみたいだな。ある汎用的な関数に何か機能を追加したいときに用いる技術だ。上書きじゃなくて、追加。上書きしたいんだったら単に定義しなおせばいいわけだもんね。関数をまるごと上書きするんじゃもったいないから少しずつデコレーションして使おうってことな。ほんで、汎用的関数をデコレーションするために使う関数のことをデコレータっていうわけだ。そういうことなら話が早い。実際に書いてみるぜ。
今回の目次



クラスとインスタンスを使ったデコレータを書いてみる

まず汎用的なものを作る。
class Foo:
    def __init__(self, name):
        self.name = name

    def call(self):
        return self.name
名前を与えてインスタンスを作ってcall()を呼べば、その名前を返してくれるクラスね。以下のように使う。
name = 'William Forsyth'
instance = Foo(name)
print(instance.call())
# William Forsyth と表示される。

使ってるうちに、名前だけじゃちょっとさみしいから枠線をつけたくなった。枠線でデコレーションをする、Deco_Wakusenデコレータを作る。上のクラスの続きに記述する。
class Deco_Wakusen:
    def __init__(self, instance):
        self.instance = instance

    def call(self):
        ret = ''
        ret += '--------------------¥n'
        ret += '  ' + self.instance.call() + '¥n'
        ret += '--------------------'
        return ret
これは上で作ったFooのインスタンスを与えてインスタンスを作る。イメージとしてはチョコレートを糖衣で包む感じか。糖衣であるDeco_Wakusenにもcall()メソッドを準備してある。このcall()では元のFooインスタンスの結果を使いつつ、枠線をつけて返すようにしてある。別にクラスを継承してるわけじゃないから、上書きにもならない。デコレーションしてるだけ。以下のように使うと、上のとおんなじようにcallしてるはずなのにデコレーションされたものが飛び出してくるぜ。
instance_wakusen = Deco_Wakusen(instance)
print(instance_wakusen.call())
# 以下のように表示される
--------------------
  William Forsyth
--------------------

今度は枠線じゃなくて、名前を大文字にして表示したくなった。やることは同じだ。
class Deco_Oomoji:
    def __init__(self, instance):
        self.instance = instance

    def call(self):
        return self.instance.call().upper()
使い方は枠線のときと同じ。
instance_oomoji = Deco_Oomoji(instance)
print(instance_oomoji.call())
# WILLIAM FORSYTHと表示される。

それでデコレータの楽しいトコなんだけど、メソッドを上書きしてるわけじゃないから、続けざまにデコレーションをすると単純に機能が追加されていくのだよ。まず汎用物を作って、枠線デコレータでデコレーションして、さらに大文字デコレータでデコレーションしてみる。
instance_wakusen = Deco_Wakusen(instance)
instance_wakusen_oomoji = Deco_Oomoji(instance_wakusen)
print(instance_wakusen_oomoji.call())
# 以下のように表示される。
--------------------
  WILLIAM FORSYTH
--------------------
ビューリホー。新しい機能を使ってるわけでも、なんでもない。ただのロジックの組み方で面白い機構ができた。

上述の例ではわかりやすさのためいちいちインスタンス名を変えてたけれど、同じインスタンス名を使ってもOK。使用感がまったく変わらないままデコレートした結果が飛び出してくる。テンプレートとしてまとめるとこんな感じか?
class 汎用:
    def デコレーションされるメソッド(self):
        なんちゃらかんちゃら

class デコレータ:
    def __init__(self, instance):
        self.instance = instance

    def デコレーションするメソッドと同名のメソッド(self):
        self.instance.メソッド()を使用しつつ
        前後に機能を書き加えて結果をreturn

instance = 汎用()
instance = デコレータ(instance)
instance.デコレーションされたメソッド()



クラスはちと大袈裟だからふつーの関数で書いてみる

いやお前いちいちクラス使うことはねーだろということで関数でやってみる。
def call(name):
    return name
シンプルどころの話じゃない。
name = 'William Forsyth'
print(call(name))
# William Forsyth

枠線デコレータとしてはこういう関数を用意。
def deco_wakusen(func):
    def call_kari(name):
        ret = ''
        ret += '--------------------¥n'
        ret += '  ' + func(name) + '¥n'
        ret += '--------------------'
        return ret
    return call_kari
関数バージョンはクラスよりシンプルになるかと思いきや、ネストのある関数が登場しちまった。けれどやってることはクラスバージョンと同じで、「もともとの関数を引数にして、内部でその関数をデコレーションした関数を作成し、その関数を返」してるだけ。関数を呼び出してる側からすれば、「関数投げたら機能追加されて返ってきたわ」みたいな感じ。
call_wakusen = deco_wakusen(call)
print(call_wakusen(name))
# こうなる。
--------------------
  William Forsyth
--------------------

大文字デコレータ。
# 元の関数を投げて、
def deco_oomoji(func):
    # 元の関数を利用しつつデコレーションする関数を作って、
    def call_kari(name):
        return func(name).upper()
    # デコレート済みの関数を返す。
    return call_kari
使い方。
call_oomoji = deco_oomoji(call)
print(call_oomoji(name))
# WILLIAM FORSYTH

重ねがけ。
call_wakusen = deco_wakusen(call)
call_wakusen_oomoji = deco_oomoji(call_wakusen)
print(call_wakusen_oomoji(name))
# こうなる。
--------------------
  WILLIAM FORSYTH
--------------------

まとめるとこう。
def デコレーションされる汎用関数():
    なんちゃらかんちゃら

def デコレータ関数(func):
    def ここの名前はどうでもいい():
        funcを使用しつつ
        前後に機能を書き加えて結果をreturn
    作った関数をreturn

汎用関数 = デコレータ関数(汎用関数)
汎用関数()



@を使って書いてみる

おいデコレータ実現できちゃったじゃねーか。どこにも@が出てこないぞ、というわけなんだが、これは上述のスクリプトを簡単に書くために使えるようだ。具体的には以下。
@deco_wakusen
def call(name):
    return name
こういう風に書くと、call()関数がdeco_wakusenデコレータでデコレーションされたことになるようだ。大文字のほうも重ねがけするならこう。
@deco_wakusen
@deco_oomoji
def call(name):
    return name
直後にこう使える。
print(call(name))
# 結果は以下。
--------------------
  WILLIAM FORSYTH
--------------------

というわけでな、冒頭で俺の目に留まった記述は、関数foo()の中身を、どっかにあるproperty()という関数で加工しているんだよーということを表していたわけだ。これの直後にfoo()を実行したら、fooに書いてあることそのままじゃなくて、propertyでデコレーションされた結果が返ってくるのだろう。いやー、調べる前に実行していたらえらい混乱が俺を襲っていたぜ、ハッハッハ。
@property
def foo(self):
    なんちゃらかんちゃら

# 実行したら、「なんちゃらかんちゃら」じゃなくて
# property()で加工されたなんちゃらかんちゃらが
# 起こる。(たぶん)



さて、みっつの章に分けてまとめてみたわけだが、最初のふたつと@使用バージョンでは趣が異なるのが気になる。俺がデコレータという概念を調べて実際に試してみた「クラスバージョン」と「関数バージョン」は、「まず汎用的な関数があり、それをあとから目的に合わせて異なるパッチを作って当てていく」という状況想定で書いてある。いっぽう@でデコレーションを簡易化しちゃうぜバージョンは、「まず汎用的なデコレータがあり、それを必要に応じて装着していく」という状況を想定しているように見える。たとえばデコレータが「関数実行結果のログをとっておく」みたいな内容だとしたら断然後者のほうが使いやすいだろう。でも前者の登場機会だっておんなじくらいあるんじゃねえ? 俺が何に引っかかってるかっていうとさ、「汎用関数が最初にある」型と「汎用デコレータが最初にある」型のうち、後者にだけ@を使った省略的書き方があるのはなんでなんだろう? ってとこなんだよ。俺がデコレータという概念を知って自然に発想した型が前者だったから、余計にね。これについては特に答えが見つかんなかった。まあ当初の目的は果たせたのでグーとする。1日1pythonどころか、このスクリプト完成にこぎつけるのには一週間くらいかかっちゃったけどこれにて閉幕。




| 緑色 | プログラミング | comments(0) |
| カテゴリ:プログラミング |
C# Python 乱数生成ノート



C#の乱数生成はシード値を自分で設定しないといけないってのが新鮮だったぜ。



範囲指定(startからend-1まで)で乱数を取り出す関数。
private int get_random_number(int start, int end, int seed)
{
    var random = new System.Random(seed);
    return random.Next(start, end);
}
これを一回だけ実行するんならシンプルなんだが、seedが同じだと毎回おんなじ結果がでちまうので、なんべんも同時に実行する場合はseedを毎回変える必要がある。たとえば100回サイコロを振る処理はこう。
// seedの初期値を「プログラム開始からのミリ秒数」とする
int seed = Environment.TickCount;

// 出目の記録用カウンター
var arr = new int[] { 0, 0, 0, 0, 0, 0 };

// 1回振るたびにseedに1足していく
for (int i = 0; i < 100; i++)
{
    int dice = get_random_number(0, 6, seed + i);
    arr[dice]++;
}

// 結果を表記
Console.WriteLine("{0}, {1}, {2}, {3}, {4}, {5}",
    arr[0], arr[1], arr[2], arr[3], arr[4], arr[5]);
結果は以下。
16, 19, 16, 15, 18, 16
一様分布になってるので意図通りだ。オーケイ。



で、pyてょんちゃんで同じことするとこう。
import random
lis = [0,0,0,0,0,0]
for i in range(100):
    lis[random.randint(0,5)] += 1
print(lis)
震えるほどシンプルだ。randintの出目は第二引数を含むってとこがポイントかな。で結果は。
[16, 24, 14, 12, 16, 18]
って、アレ? なんだかC#よりもバラつきが大きい気がする。最小値と最大値で二倍の差がある。試しに10000回実行して、出目と割合を見てみた。
# 出目
[1653, 1682, 1618, 1697, 1660, 1690]
# 割合
[0.1653, 0.1682, 0.1618, 0.1697, 0.166, 0.169]
あ、これなら一様といっていいかも。C#は少数の実行でもきちんとバラけて、Pythonはそうでもない、と。言語によって乱数生成のアルゴリズムが違うってことを目の当たりにできたぜ。


| 緑色 | プログラミング | comments(0) |
| カテゴリ:プログラミング |
Python CSVファイル、CSVリソースを配列化する関数



先日の「C# CSVファイル、CSVリソースを配列化する関数」ですけどね。C#書くのは楽しめたけど、やっぱり「お気に入りのpyてょんと比べてどうも長ったらしいな」と、思ったわけよ。というわけで「1日1Python」の時間です。あっちと比べてホントに長ったらしいのか、実際にPythonで同じものを書いて試してみようじゃねーか。
あっちと同じ順番で書いてみる。
  • 1. 改行を含むCSVファイルを用意
  • 2. CSVをリソースに追加
  • 3. 今回作ったCSV二次元配列化関数
  • 4. 作った二次元配列を確認する関数を準備
  • 5. 実行
  • 6. 結果
  • (7. exe化)



1. 改行を含むCSVファイルを用意しとく
0,緑色,Midori-iro,"みどりいろ"
1,うぇる,Weruda,"ウェル
ダ"
これは前と同じ。

2. CSVをリソースに追加しとく
「リソースに追加する」ってのはC#だと「Visual Studioのリソース欄に追加する」って意味になるけど、そんなコトはpythonじゃできん。だけれど、CSVファイルをexeファイルに含めることはできないけれど、pyファイルをexeファイルに含めることは当然できることから、「CSVファイルの中身をpyファイルに埋め込んでpyファイルとしてプログラムに埋め込む」っつー手法を思いついた。以下のようなファイルを DEFAULT_CSV.py として準備。
# coding: utf-8
text = '''
0,緑色,Midori-iro,"みどりいろ"
1,うぇる,Weruda,"ウェル
ダ"
'''

3. 今回作ったCSV二次元配列化関数
import os,sys,csv,pprint
import DEFAULT_CSV       # デフォルトのCSVをこういう形でリソース化
from io import StringIO  # 後述。python2と3で読み込み方が違うトコ注意

# カレントディレクトリをプログラムのある場所、
# あるいはexeファイルの場所に移動する
def cd_():
    # cx_freezeで固めるとコレがTrueになる。
    if hasattr(sys, 'frozen'):
        os.chdir(os.path.dirname(sys.executable))
    else:
        os.chdir(os.path.dirname(os.path.abspath(__file__)))
cd_()

# 二次元配列化の関数
def foo(csv_path, default_csv_module):
    lis = []
    if os.path.isfile(csv_path):
        # しーえすぶい.csvがあればそれを使う
        print('しーえすぶい.csvを使います')
        with open(os.path.realpath(csv_path), 'r', encoding='UTF-8') as f:
            for row in csv.reader(f):
                lis.append(row)
    else:
        # ない場合はリソース(DEFAULT_CSV.py)を使う
        print('DEFAULT_CSV.pyを使います')
        for row in csv.reader(StringIO(default_csv_module.text.strip())):
            lis.append(row)
    return lis
カレントディレクトリ移動は「一石二鳥」とか「DialogFrame」あたりで学んだマイノウハウ。exe化するスクリプトには、なんとなくこれをつけておかないと不安になっちゃうだけで、本件にはあんまり関係ない。
StringIOモジュールは、手順2で用意した文字列をファイルオブジェクトに変換するのに必要。csv.reader()は文字列を直接扱ってはくれないようで、このワンクッションがいるみたい。

4. ついでに、作った二次元配列を確認する関数を準備
pprintを使うんで作る必要なし。pprintサンにはいつもお世話になってます。

5. 実行
# リソース用の引数にはモジュールをそのまま渡す
a = foo('しーえすぶい.csv', DEFAULT_CSV)
pprint.pprint(a)

6. 結果

オーケイだね。

7. exe化
ついでに今回のスクリプトのexe化に使ったcx_freeze設定を。以下のexe化ファイルを
python cx_freeze.py build
で実行。
# cx_freeze.py
import sys
from cx_Freeze import setup, Executable
exe = Executable(
    script = 'csv_read.py',
    # 出力が欲しいプログラムではbaseをNoneにするコト。
    base = None,
)
setup(
    name = 'CSVread',
    version = '0.1',
    executables = [exe],
)



8. 結論
長さでいえば当然、pythonに軍配が上がったかな。でもそれ以上に、CSV文字列を埋め込んだpyファイルをC#でいうところのリソースとして扱うっつー思いつきが面白かった。この我流感!


| 緑色 | プログラミング | comments(0) |
   1234
567891011
12131415161718
19202122232425
2627282930  
<< November 2017 >>
+ みろりHP内検索
+ 閲覧記事
+ 過去記事アーカイブ
+ カテゴリ
+ 年月選択
  • 2017年 11月 (7)
  • 2017年 10月 (4)
  • 2017年 09月 (6)
  • 2017年 08月 (6)
  • 2017年 07月 (8)
  • 2017年 06月 (4)
  • 2017年 05月 (7)
  • 2017年 04月 (8)
  • 2017年 03月 (7)
  • 2017年 02月 (10)
  • 2017年 01月 (6)
  • 2016年 12月 (8)
  • 2016年 11月 (8)
  • 2016年 10月 (5)
  • 2016年 09月 (5)
  • 2016年 08月 (7)
  • 2016年 07月 (9)
  • 2016年 06月 (6)
  • 2016年 05月 (8)
  • 2016年 04月 (10)
  • 2016年 03月 (10)
  • 2016年 02月 (8)
  • 2016年 01月 (9)
  • 2015年 12月 (9)
  • 2015年 11月 (6)
  • 2015年 10月 (5)
  • 2015年 09月 (4)
  • 2015年 08月 (8)
  • 2015年 07月 (5)
  • 2015年 06月 (3)
  • 2015年 05月 (7)
  • 2015年 04月 (8)
  • 2015年 03月 (12)
  • 2015年 02月 (8)
  • 2015年 01月 (4)
  • 2014年 12月 (5)
  • 2014年 11月 (5)
  • 2014年 10月 (7)
  • 2014年 09月 (4)
  • 2014年 08月 (7)
  • 2014年 07月 (6)
  • 2014年 06月 (4)
  • 2014年 05月 (12)
  • 2014年 04月 (9)
  • 2014年 03月 (6)
  • 2014年 02月 (6)
  • 2014年 01月 (8)
  • 2013年 12月 (7)
  • 2013年 11月 (10)
  • 2013年 10月 (10)
  • 2013年 09月 (9)
  • 2013年 08月 (11)
  • 2013年 07月 (10)
  • 2013年 06月 (9)
  • 2013年 05月 (15)
  • 2013年 04月 (11)
  • 2013年 03月 (5)
  • 2013年 02月 (7)
  • 2013年 01月 (6)
  • 2012年 12月 (9)
  • 2012年 11月 (10)
  • 2012年 10月 (10)
  • 2012年 09月 (4)
  • 2012年 08月 (2)
  • 2012年 07月 (7)
  • 2012年 06月 (13)
  • 2012年 05月 (13)
  • 2012年 04月 (15)
  • 2012年 03月 (4)
  • 2012年 02月 (12)
  • 2012年 01月 (9)
  • 2011年 12月 (5)
  • 2011年 11月 (13)
  • 2011年 10月 (2)
  • 2011年 09月 (2)
  • 2011年 08月 (1)
  • 2011年 06月 (1)
  • 2011年 05月 (4)
  • 2011年 04月 (10)
  • 2011年 03月 (8)
  • 2011年 02月 (11)
  • 2011年 01月 (14)
  • 2010年 12月 (14)
  • 2010年 11月 (17)
  • 2010年 10月 (17)
  • 2010年 09月 (19)
  • 2010年 08月 (22)
  • 2010年 07月 (18)
  • 2010年 06月 (16)
  • 2010年 05月 (19)
  • 2010年 04月 (15)
  • 2010年 03月 (22)
  • 2010年 02月 (18)
  • 2010年 01月 (18)
  • 2009年 06月 (2)
  • 2009年 04月 (1)
  • 2007年 12月 (10)
  • 2007年 11月 (7)
  • 2007年 10月 (9)
  • 2007年 09月 (4)
  • 2007年 07月 (5)
  • 2007年 06月 (11)
  • 2007年 05月 (6)
  • 2007年 04月 (4)
  • 2006年 01月 (20)
  • + ブックマーク
    + 最近のコメント
    + アクセスカウンター
    全体(since 2010.02.03.)
    今日… 昨日…