pygtkとかpycairoを入れるときのメモ

BKChemという分子構造式エディタがあって、pythonで書かれているうえに、スクリーンショットみると構造なんかも綺麗。で、バッチ処理もできるらしいので、ちょいとこれを使ってみるかなとWindowsのバイナリ配布のやつを入れてみた。

だが、ありがちなことに、このバイナリ版はバッチ処理が出来なかったのでソースから入れたけどpycairoを要求してきたのでGTK関連を一式インストール。

GTK+のバイナリはgtk.orgからAll-in-one bundleってのを落としてきて、c:\gtk2とでも名前を変えてパスを切っておく。

で、python bindingはここのを使った。というよりeasy_installではうまく入らなかった。

これでBKChemはすんなりインストールできる。実行ファイルはC:\Python25\Lib\site-packages\bkchem\bkchem.pyだった。

bkchem.py -b batch.py args

という書式でバッチ処理が行われるようになったのでsdf2pngみたいな処理を行わせればcdkの代わりに使えるはず。

pycairo自体も結構面白そうなのでsampleをやってみた。

triangle

他にもチュートリアルがあるのであとで読もう。

pycairoでアンパンマン

普段は娘にお絵かき先生で散々描かせられているのでアンパンマン、しょくぱんまん、バイキンまん、カレーパンまん、ドキンちゃん、ホラーマン位は鼻歌混じりでかけるようになってる。

というわけで、アンパンマンをpycairoで。

import cairo
import math

pi = math.pi
WIDTH, HEIGHT = 400, 400

# Setup Cairo
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT)
ctx = cairo.Context(surface)

# Set thickness of brush
ctx.set_line_width(15)
ctx.set_source_rgba (1, 0.2, 0.2, 0.7);
ctx.arc(200, 210, 180, 0, 360 * (pi/180.0));
ctx.stroke()

#左目と眉
ctx.arc(125, 150, 50, -180 * (pi/180.0), 0 * (pi/180.0));
ctx.stroke()
ctx.arc(125, 170, 30, -180 * (pi/180.0), 360 * (pi/180.0));
ctx.fill()

#右目と眉
ctx.arc(275, 150, 50, -180 * (pi/180.0), 0 * (pi/180.0));
ctx.stroke()
ctx.arc(275, 170, 30, -180 * (pi/180.0), 360 * (pi/180.0));
ctx.fill()

#はな
ctx.arc(200, 250, 40, 0, 360 * (pi/180.0));
ctx.stroke()

#ほっぺ
ctx.arc(90, 250, 40, -90 * (pi/180.0), 90 * (pi/180.0));
ctx.stroke()
ctx.arc(310, 250, 40, 90 * (pi/180.0), -90 * (pi/180.0));
ctx.stroke()

#くち
ctx.arc(200, 305, 60, 0 * (pi/180.0), 180 * (pi/180.0));
ctx.close_path()
ctx.stroke()

#ctx.arc(250, 100, 50, -90 * (pi/180.0), 90 * (pi/180.0));


# Output a PNG file
surface.write_to_png("anpan.png")

anpanman

pycairoで一筆書きってのも面白いかも。

あと、タブレットなお絵かき先生に、flickrっぽいアップロードサイトを組み合わせたサービスはどうだろうか?

お絵かき先生2.0ってな感じで、子供の落書きとその成長に合わせた写真をタグ付けしながら、アルバムにしたりできるサービスとデバイスを一緒に売りつけるみたいな。

pythonでファイル出力と標準出力を切り替える

コマンドラインのツール書いてるとデフォルトは標準出力に書き出して、オプションでファイル名を指定したときにファイルに書き出したいときはよくあることだと思う。 で、そんなコードの定石はあるのだろうかと。オプションはoptparseやgetoptを利用すればよいのだろうが、、、

import sys,getopt

opts, args = getopt.getopt(sys.argv[1:], "o:", ["output="])

f = sys.stdout

for o, a in opts:
    if o in ("-o", "--output"):
        f = open(a,'w')

f.write("python python\npppppython")

とりあえずこんな感じで書いてみたがもっといい書き方はあるのかな。

みえないラムダが見えてきたヨ(あと括弧)。

SICPのおともにpythonインタプリタとかperlshとかmochikitのインタプリタなんかを併用すんだけど。

mapで遊んでみるわけだ、

perlだと

main[118]$ @numbers = (1,2,3,4,5)
main[120]$ map $_+5 , @numbers
6
...
10

なわけで、pythonだと

>>> numbers = [1,2,3,4,5]
>>> map(lambda (x): x +5 , numbers)
[6, 7, 8, 9, 10]

さて、SICPでググルとすぐに目に飛び込んでくる『計算機プログラムの構造と解釈』についてに引用されている、あのフレーズを思い浮かべる。

弟子が尋ねた。「先生、私は先生がカッコをまるで魔術師の ように扱っているのを常々敬服しています。どうすれば先生のようになれ るのでしょうか?」 師「えっ? カッコ? あ、そうか。そんなものもあったな。いやあ、 すっかり忘れておったわ」

perlだと忘れてるどころか、そもそも括弧もないしラムダもないわけだ。でさらに、YAPC2006でのバベルの話が頭をよぎった。

つまり最近lispが楽しいのはperlのせいに違いないと。

ProductName 計算機プログラムの構造と解釈
ジェラルド・ジェイ サスマン,ジュリー サスマン,ハロルド エイブルソン
ピアソンエデュケーション / ¥ 4,830 ()
在庫あり。

しかし、SICP in other languagesにperlがないですな。

pythonでHTTPのgzipデータを読み込む

Fetching PDB files remotely in pure Python codeを見つけて、 数行でpdbのファイルをフェッチできるなんてやるなRCSBとpythonのコンボとか思ったんだけど、コメントに「生でフェッチすんのは環境に悪いでよ」とか書いてあった。

確かにPDB界ではgzip圧縮したpdbファイルをとってきてローカルで展開すんのが昔からのナラワシだよなと、試しにgzとかZをくっつけてwgetしてみるとその通りのファイルがダウンロードできた。をを。

で、おーこれは、数行スクリプトにgzipモジュールかませばいいだけなんじゃなかろうかと思て書き書きしてみたが、これってファイルしかとれないのかな?

zlibならどうだといじってみたけど、

>>> import urllib
>>> import zlib
>>> url = 'http://www.rcsb.org/pdb/files/1ab6.pdb.gz'
>>> text = urllib.urlopen(url).read()
>>> zlib.decompress(text)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
zlib.error: Error -3 while decompressing data: incorrect header check

なんかヘッダがーっておこられた。やっぱgzipでなんとかすべきなのか? ともうちっと追いかけたらGzipFileのほうを使えばよいらしいことに辿りついた。

>>> import gzip, StringIO, urllib
>>> url = 'http://www.rcsb.org/pdb/files/1ab6.pdb.gz'
>>> content = urllib.urlopen(url).read()
>>> sf = StringIO.StringIO(content)
>>> dec = gzip.GzipFile(fileobj=sf)
>>> data = dec.read()

ちと長いがこれで読めた。というわけで、

>>> import urllib, StringIO, gzip
>>> def fetch_pdb(id):
...     url = 'http://www.rcsb.org/pdb/files/%s.pdb.gz' % id
...     content = urllib.urlopen(url).read()
...     sf = StringIO.StringIO(content)
...     return gzip.GzipFile(fileobj=sf).read()

ちょっと長くなったけど帯域にやさしい。

pythonでY-combinator(Z-combinator?)

SICPも2章に入り、お約束らしいchurch数のあたりでプチ詰まり。

とか参考にしながら、後者のほうはperlのコードがあるから理解しやすいなぁとかいいながら読み進めるが、途中からさっぱりわからん。というより、Y-combinatorを理解しないと先に進めなさそうなので、Y CombinatorTuringとChurchの狭間でを行ったりきたりしながらくらいつく。

一通り理解した気がしたところで、perlのZ-combinatorをpythonで書いてみた。

まず階乗のpython2.5から導入されたという、3項演算子を使ってみた。

>>> def f(n):
...     return 1 if n < 2 else n * f(n-1)

こんな感じで書けるがなんか、、、perlの三項演算子のほうが好き。

で、早速Z-combinator

Z = lambda f: (lambda x : lambda y : f(x(x)))(lambda x : lambda y : f(x(x)))
fact = lambda f : lambda n :  1 if n < 2 else n * f(FORCE)(n-1)
FORCE = lambda a : a

実行

>>> Z(fact)(FORCE)(7)
5040

pythonはラムダ式が書けるのでperlよりはすっきりと書ける感じ。

一応pythonでもY-combinatorの形で実行したけどこれは無限ループした。

Z = lambda f: (lambda x : f(x(x)))(lambda x : f(x(x))) 

perlやpythonのばあい、クロージャにして無理やり遅延させるので

our $FORCE = sub { my $a = shift; $a };

評価が先送りになんのはわかるんだけど、schemeで

(f 5)

((lambda (x) (f x)) 5)

にすると遅延するのがよくわからんかった。書き方変えてるだけにしか見えないんだけど。これはクロージャと同じものなのだろうかそれとも評価の順番が違うからなのか?

pythonでグラフ表現

networkxで遊んでみた。なんか丁度いいネットワークモデルがあればよかったんだけど、なにも思い当たらないのでランダムに生成。 IPythonで。ちなみに僕のIPythonは-pylabオプションがつけてあるのでfrom pylab import *が不要。

In [1]: from random import choice,shuffle
In [2]: from networkx import *
In [3]: G=XGraph()
In [4]: sample = range(1,100)
In [5]: shuffle(sample)
In [6]: j=range(1,100)
In [7]: for i in sample:
   ...:     G.add_edge(i,choice(j))
   ...:
   ...:
In [8]: draw(G)

networkx

pythonでgzipファイルをバッファなして読む

gzipで圧縮した状態で3G超のファイルを読み込むのにpython2.2だと

import gzip,sys
for line in gzip.GzipFile("gzippped_txt.gz"):
    sys.stdout.write(line)

ができなくて焦ったがpython2.5だと問題なくOK。

pythonのスコープではまる

ちょっとはまった。結局オブジェクト作って解決しといたけど気持ち悪いので少し調べた。(でも未解決)

クロージャでカウンタを考える。Open Source WEBを参考に

perlだと

sub make_counter {
  my $c = $_[0] || 0;
  return sub { $c++ }
}

gaucheだとこんな感じ

(define (make-counter n)
  (let1 c n
    (lambda () (inc! c))))

でもpythonだとだめなの

def make_counter(n):
    c = n
    def counter():
        c = c + 1
        return c
    return counter

スコープの洗礼をうけた。参照できても代入できないのでglobalにする必要が。

def make_counter(n):
    c = n
    def counter():
        global c
        c += 1
        return c
    return counter

ctr = make_counter(5)
ctr()
ctr()

Traceback (most recent call last):
  File "C:\home\kzfm\test.py", line 10, in <module>
    ctr()
  File "C:\home\kzfm\test.py", line 5, in counter
    c += 1
NameError: global name 'c' is not defined

とかいってglobalでも駄目だ。

pythonでクロージャ

昨日の続き

ウィキペディアによるとリストを参照すればいいらしいので。

def make_counter(n):
    c = []
    c.append(n)
    def counter():
        l = c.pop()
        l += 1
        c.append(l)
        return l
    return counter

ctr1 = make_counter(5)
ctr2 = make_counter(3)
print "ctr1: ", ctr1()
print "ctr1: ", ctr1()
print "ctr1: ", ctr1()
print "ctr2: ", ctr2()
print "ctr2: ", ctr2()
print "ctr1: ", ctr1()

さて実行

$ ./make_counter.py 
ctr1:  6
ctr1:  7
ctr1:  8
ctr2:  4
ctr2:  5
ctr1:  9

おーできた。でもこれだったらクラスのほうが分かりやすいなぁ。

class Counter:

    def __init__(self,num):
        self.c = int(num)

    def count(self):
        self.c += 1
        return self.c

LEGBルールってなんか分かりにくいな。単なる慣れなのか?