Pythonで被はてなブックマーク数を取得する

2010年5月31日 19:43

 Pythonのxmlrpclibモジュールで、指定したURLの被はてなブックマーク数を取得する例。簡単ですね。

 server.bookmark.getCount()はURLをキー、被はてブ数の値とするdictionaryの形で返してくれるので、返ってきたデータの参照も楽勝です。URLの一覧を引数の形で与えなければならないのがなんか引っかかりますが……。

#!/usr/bin/env python

import xmlrpclib

def main():
    uri = "http://b.hatena.ne.jp/xmlrpc"
    server = xmlrpclib.ServerProxy(uri)
    urls = ("http://sourceforge.jp/magazine/10/04/26/0244255",
             "http://sourceforge.jp/magazine/10/04/27/0326224",
             "http://sourceforge.jp/magazine/10/04/30/0243232")

    t = server.bookmark.getCount(*urls)
    for item in t:
        print item, t[item]

if __name__ == "__main__":
    main()

Pythonのwith構文と__enter__、__exit__

2010年5月18日 20:27

 Pythonのwith構文がいまいち掴めなかったので、ざっとまとめてみた(いまさらながら)。ドキュメントはPython リファレンスマニュアルの7.5 with 文にある。

 withを使ったコード例は、下記のような感じ。

c = ClassHogeHoge()
with c:
    c.foobar()

 上記のコードは、下記と等価となる。

c = ClassHogeHoge()
c.__enter__()
c.foobar()
c.__exit__()

 つまり、withに続くインデントブロックを実行する前に指定したオブジェクトの「__enter__()」メソッドを呼び出し、実行後に「__exit__()」メソッドが暗に呼び出される、という仕組み。

 __enter__()と__exit__()の定義は、Python リファレンスマニュアルの3.4.9 with文とコンテキストマネージャにある。__enter__()の引数はselfのみだが、__exit__()はself、exc_type、exc_value、tracebackの4つの引数をとる。withに続くインデントブロックが正常に実行された(つまり、例外が送出されなかった)場合、(self以外の)引数にはNoneが与えられる。なにか例外が発生した場合、その例外に関する情報が与えられるらしい。

 また、「with hogehoge as foo:」のような形でwith文を利用する場合、__enter__()の戻り値がfooに代入される。__exit__()の戻り値は例外処理の伝搬制御に使われ、Falseの場合例外が発生た場合でも例外を伝搬させず、Trueを返すと例外が伝搬されるとのこと。

 下記、使用例。

class CacheDB(object):
    DB_FILE = "database/db_dat"

    def __init__(self):
        self.con = None
        self.cur = None

    def __enter__(self):
        self.con = sqlite3.connect(self.DB_FILE)
        self.cur = self.con.cursor()

    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type:
            self.con = None
            self.cur = None
            return False
        self.con.commit()
        self.cur.close()
        self.con.close()
        self.con = None
        self.cur = None
        return True

    def add(self, foo, bar, hoge):
        try:
            self.cur.execute("""insert into data ( foo, bar, hoge )
                           values (?, ?, ?);""", (foo, bar, hoge))

def main():
    usage = "%s logfile" % sys.argv[0]
    db = CacheDB()
    try:
        fname = args[0]
    except IndexError:
        sys.exit(usage)

    f = open(fname, "r")
    with db:
        for l in f:
            term = l.strip().decode("utf-8").rsplit("\t", 3)
            db.add(foo=term[0],
                   bar=term[1],
                   hoge=term[2])

if __name__ == '__main__':
    main()

 タブ区切りのデータファイルを1行ずつ読んでデータベースに突込む、という処理。withを使うことで、データベースアクセスの準備→データ挿入→コミットという流れをきれいに実装できました。

日本語リファレンスには書いてない話:urllibとurllib2の違いってなんだ

2010年2月10日 00:51

 Pythonでは、HTTPやFTPなどでファイルの送受信をするモジュールとして「urllib」と「urllib2」が用意されている。使い方も似ていて、どちらも引数としてURLを与えてurlopen()関数を呼び出すと自動的に対応したプロトコルでURLにアクセスしてデータを読み出せる、というものである。しかし、似たような名前で似たような機能を持つこの2つ、何が違うのかが明確にはドキュメントに記述されていない。そのためどちらを使うべきか迷って、そのたびにGoogleのお世話になるという状況だったのでざっとまとめてみよう。

Mako Templaters for Pythonメモ1:Makoってなに?

2010年2月2日 15:32

 最近Pythonのテンプレートエンジン「Mako」を触ってるんだけど、日本語の情報が全然ないのでまとめてみる。

Mako公式Webサイト

Mako公式Webサイト

Pythonネタ:unittestを使う

[]
2009年7月27日 17:50

 Python標準のユニットテスト機能、「unittest」の使い方メモ。

目的

 unittestはPythonで作成したクラスの特定の関数や、機能の動作確認に利用できる機能だ。詳しくは ドキュメントを読めばすぐに分かる が、unittestクラスの派生クラスを作り、そのクラスの関数としてテストコードを記述してやると、簡単にユニットテストができる、というもの。

 メインの実装コードにprint文などを挿入したり、テストコードを挿入しても良いのだが、それだとテストコードの再利用が難しかったり、いったんバグ修正を行ってテストコードを削除した後に再度バグが発生したりした場合に二度手間になったりする。そのため、テストコードはなるべくunittestにまとめて記述しておくとデバッグや実装、テストが楽になりますよ、というお話。

使い方

 基本的な使い方はこれまたオンラインドキュメントにあるのだが、自分は下記の形をよく使っている。

import random
import unittest

class TestSequenceFunctions(unittest.TestCase):
    def setUp(self):
        # ここに各テスト関数を実行する前に呼び出す処理を書く。
        # 通常は共通のデータの準備とかを書くことが多い

   def tearDown(self):
        # ここに各テスト関数を実行した後に呼び出す処理を書く。
        # 通常は共通のデータの後片付けとかを書くことが多い

    def test_hogehoge(self):
        """test for hogehoge テストの内容をコメントに入れる"""
        # テストコード1をここに書く

    def test_foobar(self):
        """test for foobar テストの内容をコメントに入れる"""
        # テストコード2をここに書く

# do unittest
# テストオブジェクトを作成
suite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)

# テスト実行。出力するメッセージレベルはverbosity引数で設定できる
unittest.TextTestRunner(verbosity=2).run(suite)

 テストコード内でテストが期待したとおりの処理を行っているかどうかは、unittest.TestCaseクラス内で用意されているasert/fail関数を使うのが好ましい。詳しくは「pydoc unittest」等で確認できるが、たとえば二つの引数の値が等しくない場合にエラーを出すには「assertEqual(引数1、引数2、エラーメッセージ)」関数を使う。

 そのほか、unittestにはレポート機能などもあるが、基本的には上記さえ押さえておけば事足りるはず。