辞書のgetとsetdefault

辞書に存在しないキーを普通に[]で参照するとKeyErrorになります。

>>> d = {}
>>> d['foo']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'foo'

d.get()を使うと、キーが存在しない場合に返却するデフォルト値を指定できます。が、dの中自体は変更しません。
d.setdefault()を使うと、キーが存在しない場合はデフォルト値を返却し、かつdの中も変更してくれます。

>>> d.get('foo', 'bar')
'bar'
>>> d
{}
>>> d.setdefault('foo', 'bar')
'bar'
>>> d
{'foo': 'bar'}

前に気になっていた

        if not statetab.has_key((w1, w2)):
            statetab[(w1, w2)] = []
        statetab[(w1, w2)].append(word)

というコードは

        statetab.setdefault((w1, w2), []).append(word)

と書き直せますね。

わかち書かないでマルコフ連鎖

マルコフ連鎖プログラム - pyletの日記
以前作ったマルコフ連鎖プログラムは、実はわかち書きしないで一文字ずつの方が面白いんじゃないかという気がしてきました。

import sys, random

MAXGEN = 1000
nonword = "\n"
w1 = w2 = nonword
statetab = {}

while 1:
    line = unicode(sys.stdin.readline(), 'mbcs')
    if line == "":
        break
    for word in line:
        if (w1, w2) not in statetab:
            statetab[(w1, w2)] = []
        statetab[(w1, w2)].append(word)
        w1, w2 = w2, word

if (w1, w2) not in statetab:
    statetab[(w1, w2)] = []
statetab[(w1, w2)].append(nonword)

w1 = w2 = nonword
for i in range(MAXGEN):
    t = random.choice(statetab[(w1, w2)])
    sys.stdout.write(t.encode('mbcs'))
    w1, w2 = w2, t

実行。

昨日、近所の吉野家通の頼み方。
お前らな、150円。
大盛りねぎだくギョク。これ最強。
素人は、牛鮭【ぎゅうしゃけ】定食でも食ってもおかしくなんかと思ったんです。
そこでまたぶち切れです。吉野家通の頼み方。
お前らド素人は、すっこんでろ。
そこでまたぶち切れです。
よーしパパ特盛頼んじゃねーよ、ボケが。
しかしこれを頼むと次からその席空けろと。
吉野家ってのはな、つゆだくってのはな、150円だよ。
で、やっと座れない。
よーしパパ特盛頼んじゃうんか人がめちゃくちゃいっぱり、
ねぎだくない。問い詰めたい。
で、よく見たらな、つゆだくで、よく見たらなんだよ。
150円。
得意げな顔してるんでろ。
そこでまたぶち切れです。
もう見てらんない。問い詰めたいだけちゃいってのはな、150円やるからその席空けろと。
お前らド素人にはお薦め出来ないんでろ。
お前、つゆだくって言いたい。小1時間問いたいだけちゃうぞー、とか書いてあるんでろ。
吉野家通の頼み方。
で、やっと座れたかと問いたいのかと思った。
そしたらな、つゆだくを食いたい。小1時間問い詰めたい
で、とかもいるし。一家4人で吉野家に来てなさいってるの。もう見てらんない。
お前らな、150円引き如きで普段来てない。
150円。
お前らド素人は、すっこんでろ。
もう見てらんな雰囲気がいいんじゃうんか親子連れとか書いてあるんです。
よーしパパ特盛頼んじゃねーんだよ。ボケが。
あのな、つゆだく、これが通の俺からその席空けろと。
お前らなんか人がめちゃうぞー、とか書いてあるんです。
まあお前らなんか人がめちゃいって言いたい。問い詰めたい。小1時間問いたい。小1時間問い詰めたい。問いたい。
吉野家行った。
よーしパパ特盛頼んじゃねーんだよ。ボケが。
よーしパパ特盛頼んじゃねーんだよ。
で、よく見たら、隣の奴が、大盛つゆだくってるの。もうね、アホかと思ったらなんか人がめちゃくちゃいっぱり、
ねぎだく、これ。
素人にはお薦め出来ない、
ねぎが多めに入ってるべきなんかと。
なんか垂れ幕下がってる。そんない。
Uの字テーブルの向かいに座った奴といつ喧嘩が始まってる。そんな雰囲気がいいんです。
Uの字テーブルの向かいに座ったんです。
大盛りねぎが多めに入って言いたい。
で、やっと殺伐として何が、大盛りねぎだくない。

連鎖数2でもすごい破壊力(?)です。「してるんでろ」とか「出来ないんでろ」という特徴的な語尾は「すっこんでろ」から来てるんですね。

プログラム的に工夫したのは、最後の出力をprint t,にすると一文字ずつ空白があいてせっかくのコピペが台無しなので、5分悩んでsys.stdout.writeにしてみました。

DBAPI勉強中(2)

色んなデータ型を試してみます。

import psycopg2

con = psycopg2.connect("dbname=testdb")
cur = con.cursor()
cur.execute("drop table if exists t")
cur.execute("""
        create table t(
            i integer,
            r real,
            n numeric,
            v varchar(10),
            t text,
            d date,
            ti time,
            ts timestamp)
            """)
cur.execute("""
        insert into t values(
            1,
            1.2,
            2.2,
            'hello',
            'world',
            current_date,
            current_time,
            current_timestamp)
            """)
cur.execute("select * from t")
print cur.next()

結果。

(1, 1.2, Decimal("2.2"), 'hello', 'world', datetime.date(2008, 3, 31), datetime.
time(17, 40, 34, 640000), datetime.datetime(2008, 3, 31, 17, 40, 34, 640000))

いい感じでキャストされてます。

DBAPI勉強中

PostgreSQL + psycopg2でデータベースにアクセスちゅう。

PEP 249 -- Python Database API Specification v2.0 | Python.org

いわゆるプリペアードステートメントみたいな、SQLとパラメータを別々に渡すために、DBAPIでは何種類かのフォーマットが選べるみたいです。で、デフォルトは'pyformat'になってるみたいです。

>>> import psycopg2
>>> psycopg2.paramstyle
'pyformat'

pyformatの場合は、execute()の時に値を辞書で渡します。

>>> con = psycopg2.connect("dbname=mail")
>>> cur = con.cursor()
>>> cur.execute("select %(msg)s", dict(msg="Hello"))

とすると、PostgreSQL側では、

LOG:  statement: BEGIN; SET TRANSACTION ISOLATION LEVEL READ COMMITTED
LOG:  statement: select 'Hello'

ってな感じでログが残ります。

  • トランザクションは自動的に開始されるみたいです
  • 文字列のクォートも自動的にやってくれてますね

ためしに意地悪してみると、

>>> cur.execute("select %(msg)s", dict(msg="it's a beautiful day"))
>>> cur.execute("select %(msg)s", dict(msg=None))
LOG:  statement: select 'it''s a beautiful day'
LOG:  statement: select NULL

ちゃんとエスケープしてますね。あとNoneはNULLになるようです。

2つずつ取り出すイテレータ

やっとすこしイテレータがわかってきた気がします。itertoolsはすごく面白そう。

2つずつ取り出すイテレータを作ってみました。

def take2(ls):
	i = iter(ls)
	while 1:
		yield i.next(), i.next()


>>> for a, b in take2(range(10)):
...     print a, b
...
0 1
2 3
4 5
6 7
8 9

Python で blosxom をちょっとアレンンジ

Python で blosxom ! - 女子高生ぷろぐらまーなお☆のブログ

参考に、いろいろ遊んでみました。

#!/usr/bin/env python

import os

opts = {
        'data-dir' : "data",
        'data-ext' : ".txt",
        }

class BlosxomPy:
    class Entry:
        def __init__(self, file, name, time, title, body):
            self.file = file
            self.name = name
            self.time = time
            self.title = title
            self.body = body

    def __init__(self, opts):
        self.opts = opts

    def run(self):
        def sort_by_mtime(a, b):
            return cmp(b.time, a.time)
        entries = self.entries()
        print repr([e.name for e in sorted(entries, sort_by_mtime)])

    def entries(self):
        for path in self.listfiles(opts['data-dir']):
            f = file(path)
            content = f.readlines()

            e = self.Entry(
                    file = path,
                    name = path.replace(opts['data-dir'], '').replace(opts['data-ext'], ''),
                    time  = os.stat(path).st_mtime,
                    title = content[0],
                    body  = content[1:-1])
            yield e

    def listfiles(self, dir):
        dirs = [dir]
        while len(dirs) > 0:
            dir = dirs.pop()
            for file in os.listdir(dir):
                file = os.path.normpath(os.path.join(dir, file))
                if os.path.isdir(file):
                    dirs.append(file)
                else:
                    yield file

BlosxomPy(opts).run()

ポイント。

  • yieldを使った方がpythonicな気がします。
  • でもそれだとそのままentries.sort()できなかったので、sorted()関数を使ってます。
  • Entryに__init__()を作りました。キーワード引数好き好きなので。
  • もうちょっと色々な処理をEntryクラスに移動した方が良いかも。
  • blosxomとしてはまだ全然完成してません(>_<)