Pythonのdisモジュール

逆アセンブル用のモジュール

ソースコードも200行程度で読みやすそうなので、読んでみようかと。

その前にドキュメント読んだら面白かった。

Rubyで作る奇妙なプログラミング言語でスタックマシンを作ってみた経験が理解において非常に役に立った。

ProductName Rubyで作る奇妙なプログラミング言語 ~Esoteric Language~
原 悠
毎日コミュニケーションズ / ?円 ( 2008-12-20 )


リスト内包標記

>>> def cp(l): return [e for e in l]
... 
>>> dis.dis(cp)
  1           0 BUILD_LIST               0
              3 DUP_TOP             
              4 STORE_FAST               1 (_[1])
              7 LOAD_FAST                0 (l)
             10 GET_ITER            
        >>   11 FOR_ITER                13 (to 27)
             14 STORE_FAST               2 (e)
             17 LOAD_FAST                1 (_[1])
             20 LOAD_FAST                2 (e)
             23 LIST_APPEND         
             24 JUMP_ABSOLUTE           11
        >>   27 DELETE_FAST              1 (_[1])
             30 RETURN_VALUE

ループで書いてみる

>>> def cp2(l):
...   ll = []
...   for e in l:
...     ll.append(e)
...   return ll
... 
>>> dis.dis(cp2)
  2           0 BUILD_LIST               0
              3 STORE_FAST               1 (ll)

  3           6 SETUP_LOOP              27 (to 36)
              9 LOAD_FAST                0 (l)
             12 GET_ITER            
        >>   13 FOR_ITER                19 (to 35)
             16 STORE_FAST               2 (e)

  4          19 LOAD_FAST                1 (ll)
             22 LOAD_ATTR                0 (append)
             25 LOAD_FAST                2 (e)
             28 CALL_FUNCTION            1
             31 POP_TOP             
             32 JUMP_ABSOLUTE           13
        >>   35 POP_BLOCK

  5     >>   36 LOAD_FAST                1 (ll)
             39 RETURN_VALUE

参考

ProductName エキスパートPythonプログラミング
Tarek Ziade
アスキー・メディアワークス / 3780円 ( 2010-05-28 )


Pythonのcontextlibモジュール

contextlib — with -構文コンテキストのためのユーティリティ。

functoolsのwrapsで包んだヘルパー関数を返す

def contextmanager(func):
    @wraps(func)
    def helper(*args, **kwds):
        return GeneratorContextManager(func(*args, **kwds))
    return helper

GeneratorContextManagerクラス(例外処理をのぞいて把握しやすくした)__enter__実行してからyieldして最後に__exit__が実行される

class GeneratorContextManager(object):
    """Helper for @contextmanager decorator."""

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

    def __enter__(self):
        return self.gen.next()

    def __exit__(self, type, value, traceback):
        if type is None:
            try:
                self.gen.next()
            except StopIteration:
                return
            else:
                raise RuntimeError("generator didn't stop")

実際に使う場合はこんな感じでジェネレータを包む

@contextmanager
def tag(name):
    print "<%s>" % name
    yield
    print "</%s>" % name

>>> with tag("h1"):
...    print "foo"
...
<h1>
foo
</h1>

functoolsのwraps

10.8. functools — 高階関数と呼び出し可能オブジェクトの操作

これはラッパ関数を定義するときに partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated) を関数デコレータとして呼び出す便宜関数です。

Pythonのwebbrowserモジュール

ソース読んでたらmain関数が

def main():
    import getopt
    usage = """Usage: %s [-n | -t] url
    -n: open new window
    -t: open new tab""" % sys.argv[0]
    try:
        opts, args = getopt.getopt(sys.argv[1:], 'ntd')
    except getopt.error, msg:
        print >>sys.stderr, msg
        print >>sys.stderr, usage
        sys.exit(1)
    new_win = 0
    for o, a in opts:
        if o == '-n': new_win = 1
        elif o == '-t': new_win = 2
    if len(args) != 1:
        print >>sys.stderr, usage
        sys.exit(1)

    url = args[0]
    open(url, new_win)

    print "\a"

ということなので

python -mwebbrowser -n "http://blog.kzfmix.com"

と打てば任意のURLをwebbrowserで開くことが出来る。

Pythonのinspectモジュールを読んでみた。

今日も体調がすぐれないのというか、余計ひどくなった気がする(喉痛い)ので会社を休む。有休あまりまくってるしまぁここらへんで消費するのもいいかなと。

寝ながら本を読むにも軽めのちょうどいいのがないので、ソースコードリーディングでもとinspect読んでみた

getargs,getsource,classify_class_attrsあたりは参考になった。

こんな感じでK-meansのコードがあったとする。

from itertools import groupby
from math import sqrt

zipWith = lambda f, xs, ys : [f(x, y) for x,y in zip(xs, ys)]
snd  = lambda x: x[1]
fst  = lambda x: x[0]
euclid  = lambda x, y : (x-y)**2

def distance(a,b): 
    return sqrt(sum(zipWith(euclid,a,b)))

def centroid(clusters): 
    sums = reduce(lambda x,y: zipWith(lambda a,b:a+b,x,y),clusters)
    return map(lambda x: x / float(len(clusters)), sums)

def closest(pts, pt):
    closest_ct = pts[0]
    for ct in pts[1:]:
        if distance(pt,closest_ct) > distance(pt,ct):
            closest_ct = ct
    return closest_ct

def recluster_(centroids,points):
    reclustered = [(closest(centroids,a), a) for a in points]
    reclustered.sort()
    return [map(snd,list(g)) for k, g in groupby(reclustered, fst)]

def recluster(clusters):
    centroids = map(centroid, clusters)
    concated_clusters = reduce(lambda a,b: a+b, clusters)
    return recluster_(centroids,concated_clusters)

def part(l,points):
    size = len(l)/points
    return [l[i:i+size] for i in range(0,len(l),size)]

def kmeans(k,points):
    cluster = part(k,points)
    newcluster = recluster(cluster)
    while(cluster !=  newcluster):
        cluster = newcluster
        newcluster = recluster(cluster)
    return newcluster

if __name__ == "__main__":
    pts = [[1,2,4],[1,3,3],[4,3,0],[2,5,1],[7,3,8],[0,0,0],[4,3,2],[6,1,8]]
    print kmeans(pts,3)

モジュールロード

>>> import kmeans
>>> import inspect

どんな関数があったかなぁというのは、まぁdirでわかる

>>> dir(kmeans)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', \
'centroid', 'closest', 'distance', 'euclid', 'fst', 'groupby', 'kmeans', \
'part', 'recluster', 'recluster_', 'snd', 'sqrt', 'zipWith']

引数どうだっけ?となったばあいにはgetargsつかえばいいけどコードオブジェクトを引数にとるのでfunc_codeを渡してる

>>> inspect.getargs(kmeans.distance.func_code)
Arguments(args=['a', 'b'], varargs=None, keywords=None)

distanceって何の距離計算してんだっけ(マンハッタン?ユークリッド?)ソースみたいなあってなったらgetsource

>>> inspect.getsource(kmeans.distance)
'def distance(a,b): \n    return sqrt(sum(zipWith(euclid,a,b)))\n'
>>> inspect.getsource(kmeans.euclid)
'euclid  = lambda x, y : (x-y)**2\n'

というわけでユークリッド距離で定義された距離でのK-meansのコードだということがわかる。

>>> inspect.getfile(kmeans.distance)
'kmeans.py'
>>> inspect.getabsfile(kmeans.distance)
'/Users/kzfm/python/kmeans.py'

getabsfileでファイルの場所探してemacsで開いて直接読んじゃうことのほうが多いかもしれんけど。

入門自然言語処理

ひと通り流し読みした。

職場の昼休みに読み進めてきたので結構時間がかかったし、流し読みなので理解できてないところも結構ある。

1-4章がPythonでテキスト処理するための基礎的な事柄の説明で、それ以降が英語を想定した自然言語処理。最後の方は内容が高度だが10章の「文の意味の解析」は面白かった。プログラミング言語も言語の一種だからね。12章が日本語版オリジナルでPythonでの日本語の取り扱いに関して。

ProductName 入門 自然言語処理
Steven Bird
オライリージャパン / 3990円 ( 2010-11-11 )


章末問題が充実していて、この部分を解いて理解度をチェックしながら読み進むのがよいんだろうなぁと思いつつも、独習はちょっとやだなぁと思った。勉強会の題材にはいいのかなぁと。

今は自然言語処理へのモチベーションがそれほど高まってないこともあってざっと読んでみた。

Flaskでsoaplibを使う

githubから

あとでWerkzeugのドキュメントをちゃんと読まないと何やってるかよくわからんが/soapにアクセスすればsoapサービス使えるということでいいのかな。

選曲候補を記録するスクリプトをPythonで書いた

DJ選曲術を読んだり田中フミヤのviaのダイジェストを見たりしてたら、選曲というものが楽しくなってきた。どういう曲のつながりを気持いいと思うかは結局自分をよく知ることになるので意味のある行為ではないかと思う。

で、iTunesで聴いてる曲のつながりを記録するCUIアプリをPythonで書いてみた。単にヘッドとテイルを記録するだけの有向グラフなんだけど、sqliteにぶち込んでいるので後から検索するとき便利だと思う。データが溜まったらチェインをたどりながらplaylistに書きだすようなスクリプトも用意しようかな。

あとは選曲のつながりぐあいをnetworkxとかcytoscape視覚化して見てみたいという個人的な興味もあったりするのでそういうデータが欲しかったりする。

djutil

h(head)を押すとヘッドに現在聴いてるiTunesの曲名が、t(tail)だとテイルに挿入されてヘッドとテイルが揃うとグラフとして追加されるようにしてある。q押すとquitするようにしてあるんだけどquitしないのでなんかおかしい。後でどうにかする

そのうちGitHubで管理する予定

以下コード

#!/usr/bin/python
# -*- encoding:utf-8 -*-

# kzfm <kerolinq@gmail.com>

import curses
import os
from ScriptingBridge import *

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine,Column,Integer,String,ForeignKey,UniqueConstraint
from sqlalchemy.orm import scoped_session,sessionmaker

DATABASE = '/Users/kzfm/djutil.db'
engine = create_engine('sqlite:///'+DATABASE)
Session = scoped_session(sessionmaker(autocommit=False,
                                      autoflush=False,
                                      bind=engine))
Base = declarative_base(bind=engine)

class Music(Base):
    __tablename__ = 'music'
    __table_args__ = (UniqueConstraint('title','artist'),{})
    id = Column(Integer, primary_key=True)
    title  = Column(String(128))
    artist = Column(String(128))

class Graph(Base):
    __tablename__ = 'graph'
    __table_args__ = (UniqueConstraint('head','tail'),{})
    id = Column(Integer, primary_key=True)
    head = Column(Integer, ForeignKey('music.id'))
    tail = Column(Integer, ForeignKey('music.id'))

def main(stdscr):
    if not os.path.isfile(DATABASE):
        Base.metadata.create_all()

    session = Session()

    iTunes = SBApplication.applicationWithBundleIdentifier_("com.apple.iTunes")
    stdscr.nodelay(1)
    head_music = None
    tail_music = None

    while True:
        c = stdscr.getch()
        y,x = stdscr.getyx()
        if c == ord('h'):
            artist = iTunes.currentTrack().artist()
            title  = iTunes.currentTrack().name()
            head_music = session.query(Music).filter(Music.artist == artist) \
                                             .filter(Music.title == title) \
                                             .first()
            if head_music == None:
                head_music = Music(artist=artist,title=title)
                session.add(head_music)
                session.commit()

            if tail_music != None:
                mg = session.query(Graph).filter(Graph.head == head_music.id) \
                                             .filter(Graph.tail == tail_music.id) \
                                             .first()
                if mg == None:
                    mg = Graph(head=head_music.id,tail=tail_music.id)
                    session.add(mg)
                    session.commit()

                stdscr.addstr("%s / %s -> %s / %s" % (head_music.title,head_music.artist,tail_music.title,tail_music.artist))
                stdscr.move(y+1, 0)

                tail_music = None
            else:
                stdscr.addstr("%s / %s -> [] / []" % (head_music.title,head_music.artist))
                stdscr.move(y, 0)

        elif c == ord('t'):
            artist = iTunes.currentTrack().artist()
            title  = iTunes.currentTrack().name()
            tail_music = session.query(Music).filter(Music.artist == artist) \
                                .filter(Music.title == title) \
                                .first()

            if tail_music == None:
                tail_music = Music(artist=artist,title=title)
                session.add(tail_music)
                session.commit()

            if head_music != None:
                mg = session.query(Graph).filter(Graph.head == head_music.id) \
                                             .filter(Graph.tail == tail_music.id) \
                                             .first()
                if mg == None:
                    mg = Graph(head=head_music.id,tail=tail_music.id)
                    session.add(mg)
                    session.commit()

                stdscr.addstr("%s / %s -> %s / %s" % (head_music.title,head_music.artist,tail_music.title,tail_music.artist))
                stdscr.move(y+1, 0)
                tail_music = None

            else:
                stdscr.addstr("[] / [] -> %s / %s" % (tail_music.title,tail_music.artist))
                stdscr.move(y, 0)
        elif c == ord('q'):
            exit
        elif c != -1:
            # debug
            stdscr.addstr(str(c))
            stdscr.refresh()
            stdscr.move(0, 0)

if __name__ == '__main__':
    curses.wrapper(main)

pythonでcursesプログラミング

突然pythonでcursesプログラミングがしたくなったというか端末アプリを作る時にcurses使えたほうがいいよねと。

で、cursesプログラミングするならCのドキュメント漁ったほうがいいよなということで、Linuxプログラミングを読みなおした

ProductName Linuxプログラミング―例題で学ぶUNIXプログラミング環境のすべて
ニール マシュー
ソフトバンククリエイティブ / ?円 ( 1999-03 )


一章まるまるcursesに割かれているので役に立った。買ったときには「cursesなんて使わんだろ」と読み飛ばしたのに。

結局、端末操作はキーボードがある限りなくならんし、効率的なインターフェースで在り続けるのだろうな。

iTunesの現在聴いている曲名をPythonで取得する

Scripting Bridgeを使ってみた

>>> from ScriptingBridge import *
>>> iTunes = SBApplication.applicationWithBundleIdentifier_("com.apple.iTunes")
>>> iTunes.currentTrack().artist()
u'Noisia & Alix Perez'
>>> iTunes.currentTrack().name()
u'Underprint'

つなぐ候補の三つ組とか有向グラフをデータベース化しておきたいというのがモチベーション

ProductName Native Instruments TRAKTOR PRO

Native Instruments / ?円 ( )


あとは、つなぐ情報を溜めるようなCUIを作ればよいですな。

「プログラミング Google App Engine」を読んだ

入門書ではないので、まったくの初心者向けではないですね。JavaとPythonどっちの説明も載っているけど、Python使いのほうがよりお役立ちだと思う(PythonでGAEの書籍ほとんどないし)。ちなみにJavaのGAE本は知らない。

ProductName プログラミング Google App Engine
Dan Sanderson
オライリージャパン / 3570円 ( 2011-01-24 )


Pythonのフレームワーク幾つか習得した状態でGAEやると「モデル操作わからんというかデータストア怖い」みたいな苦手意識が芽生えるんだけど、4,5,6,7と4章分がデータストアの説明に費やされているので非常に良い。

  • 4章 データストアのエンティティ
  • 5章 データストアのクエリ
  • 6章 データストアのトランザクション
  • 7章 Pythonでのデータモデリング

Flask+データストアでコード書くとき重宝しそう

あとメール、バルクデータ操作、スケジュールタスクなんかもそのうち使いそうなのでさらっと眺めておいた。

  • 11章 メールとインスタントメッセージの送受信
  • 12章 バルクデータ操作とリモートアクセス
  • 13章 タスクキューとスケジュールタスク

息子の嘔吐下痢の直撃を受けてぐったりしながらエントリ書いた。元気になったらGAEでなんかつくろう。