PIL 1.1.6
Python Imaging Libraryこと、PIL。
画像をいじったりできるアレです。
さらーっとドキュメントを見た感じだと、いろいろできそうです。
ReviewBoardの必須モジュールになっていたりとPythonで画像扱う場合は有名なんでしょうか?
画像を正方形に切り出して縮小してJpegで保存するような処理を適当に書いてみるとこんな感じ。
import sys
import Imagesrc = Image.open('test.bmp')
if src.size[0] > src.size[1]:
delta = src.size[0] - src.size[1]
box = (delta / 2, 0, (src.size[0] - delta / 2), src.size[1])
elif src.size[1] > src.size[0]:
delta = src.size[1] - src.size[0]
box = (0, delta / 2, src.size[0], (src.size[1] - delta / 2))
else:
box = (0, 0, src.size[0], src.size[1])try:
src.crop(box).resize( (300,300) ).save('out.jpg')
except IOError:
sys.exit(1)sys.exit()
GIMPで同じように縮小して、クオリティ50%で保存したJpegと比較してみるとちょっと文字とかがつぶれすぎな印象。時間があるときにもうちょっと使ってみよう。
SQLAlchemy 続き
普通にSELECT文。
まずは適当にテーブルを作ってデータを適当に投入。
mysql> select * from maker;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- +
-
-
-
-
-
-
-
-
-
-
-
-
-
id | name |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- +
-
-
-
-
-
-
-
-
-
-
-
-
-
1 | SOMY |
2 | MEC |
3 | HiTouth |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- +
-
-
-
-
-
-
-
-
-
-
-
-
-
mysql> select * from goods;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- +
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
id | maker_id | name |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- +
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
1 | 1 | Walkman |
2 | 1 | AIBO |
3 | 1 | PSP |
4 | 2 | PC-FX |
5 | 2 | スパコン |
6 | 3 | パンポン |
7 | 3 | 気になる木 |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- +
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Tableインスタンスとマッピングするクラスを定義してある状態で...
という感じでSELECT。
for obj in sess.query(Maker).order_by(Maker.id):
print obj.id, obj.name1 SOMY
2 MEC
3 HiTouth
この場合、実際にバックグラウンドで実行されるSQLは
SELECT maker.id AS maker_id, maker.name AS maker_nameこんな感じ。
FROM maker ORDER BY maker.id
次はSQLでいうとこのWHERE。
for obj in sess.query(Goods).filter_by(maker_id=1).order_by(Goods.id):
print obj.id, obj.maker_id, obj.name
1 1 Walkman
2 1 AIBO
3 1 PSP
別の書き方。
結果はさっきと同じ。
for obj in sess.query(Goods).filter(Goods.maker_id==1).order_by(Goods.id):
print obj.id, obj.maker_id, obj.name
LIKEを使う
for obj in sess.query(Maker).filter(Maker.name.like('%M%')).order_by(Maker.id):
print obj.id, obj.name
複数条件(AND)
from sqlalchemy import and_
for obj in sess.query(Goods).filter(and_(Goods.maker_id<3, Goods.name.like('%FX%'))).order_by(Goods.id):
print obj.id, obj.maker_id, obj.name
Zope3いれてみる
PyPIを見て常々、Zope関連のモジュール(特に3)が多いなーと思う。GoogleAppEngineに採用されてDjangoが大流行じゃなかったのかと。
というわけでZope3メモ。Windowsマシン前提です。
Zope2系とちがって、Windows用でもPython同梱ではない模様。あらかじめ2.4系のPythonを入れておきましょう。なければインストーラが進みません。
インストール先は問答無用でPythonインストールフォルダになります。レジストリの情報を参照するようです。
インストール後、まずはZopeのインスタンスを作らないと始まらない。
PythonインストールフォルダがC:\Python24の場合、C:\Python24\Scriptsの下にmkzopeinstance.batがインストールされているはずなのでこいつを実行する。
対話形式で、
が聞かれる。
全部ちゃんと答えると、インスタンスが生成。
インスタンスを生成したところにたくさんファイルが生成。
そこのbinディレクトリに、runzope.batがあるはずなので、そいつを実行。
ブラウザで
http://localhost:8080/にアクセスすると、そこがZope。
しかしこれだけじゃ何をして良いのやらさっぱり(´・ω・`)
ぼちぼち英文のチュートリアルでも見ながらやってみよう。
SQLAlchemy 0.5.2
メインゴールはあなたのデータベースとSQLへの考え方を変えること!
ということを目標に作られている、O'REILLYから本(ISBN:0596516142)も出てる有名O/Rマッパです。
本家のチュートリアルもWEB+DB PRESS Vol.46の記事でもSQLiteを使ってましたので、MySQL5.1で使ってみます。
準備
SQLAlchemyのインストール
普通にeasy_installで大丈夫です。これを書いている時点ではバージョン指定なしで、0.5.2がインストールされます。
easy_install sqlalchemy
セッションを作る
まずエンジンを定義して、それを元にセッションを生成〜という流れになります。
で、エンジンはsqlalchemy.create_engine関数に、RFC-1738形式のURLを渡すことで生成します。
MySQLでlocalhostのhogeデータベースに接続する場合は
という感じで。
engine = create_engine('mysql://user:password@localhost/hoge', echo=True)
echo=True としておくと実行したSQLやらを標準出力に出してくれます。デフォルトはFalseです。
sqlalchemy.orm.sessionmaker()でセッションクラスを生成します。
この戻り値がセッションのサブクラスになっているので、これをアプリケーションのグローバルスコープでやってくれとのことです。つまり、
としておけば、後はセッションがほしくなったときにその場所で
Session = sqlalchemy.orm.sessionmaker(bind=engine)
すればセッションゲットだぜ!という寸法です。クラスをオブジェクトとして利用できるPythonらしい方法だなーと。
sess = Session()
ちなみにsessionmaker()関数はキーワード引数をとり、エンジンを指定するbind以外にも
autocommit(bool型でデフォルトはFalse)やautoflushなどいろいろ指定できますようで。詳しくはこちら。
テーブル定義
テーブルはsqlalchemy.schemaパッケージのTableクラスのインスタンスとして定義します。
インスタンス化するときに、カラムの定義としてColumnクラスを、型の定義に型用のクラスを使います。この辺のものはsqlalchemy/__init__.pyでインポートされているので、特に型についてはそれを見るとどんな型があるのか一目でわかると思います。
でまぁ、こんなソースを書いて
んでこれを実行すると、
import sqlalchemy
from sqlalchemy import Table, Column, Integer, String
metadata = sqlalchemy.MetaData()
hoge_table = Table('hoges', metadata,
Column('id', Integer, primary_key=True),
Column('name', String(50)),
Column('level', Integer, server_default='1'),
mysql_engine='MyISAM',
mysql_charset='utf8')if __name__ == '__main__':
from sqlalchemy import create_engine
engine = create_engine('mysql://user:pass@localhost/hoge', echo=True)
metadata.create_all(engine)
というSQLが実行されて
CREATE TABLE hoges (
id INTEGER NOT NULL AUTO_INCREMENT,
name VARCHAR(50),
level INTEGER DEFAULT '1',
PRIMARY KEY (id)
)ENGINE=MyISAM CHARSET=utf8
mysql> describe hoges;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- +
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Field | Type | Null | Key | Default | Extra |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- +
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
id | int(11) | NO | PRI | NULL | auto_increment |
name | varchar(50) | YES | NULL | ||
level | int(11) | YES | 1 |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- +
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
となりました。
Columnクラスのキーワード引数、server_defaultに値を指定しておけば、デフォルトになるんですが、カラムの型がIntegerでもstr型で指定しないとエラーになります。
MySQLの場合はエンジンやらcharsetやらありますので、mysql_ほにゃらら=ごにょごにょ という感じで指定します。
metadataは、なんか特殊なことをするときに利用するそうで、引数なしでやっておけばデフォルト値が全部使われます。
Tableクラスのインスタンスとして定義したテーブルの情報はクラスとマッピングするときに使いますので、上で書いたソースは別のファイルからimportして使えるようにしています。
ちなみにテーブルを作るだけならセッションを生成する必要はありません。
テーブルとクラスのマッピング
Tableクラスのインスタンスとして定義したテーブルを、クラスに紐付けます。
クラスは全部の属性を定義する必要は必ずしもなく、中身がpassしてるだけのクラスでも、カラム名をそのままオブジェクトの属性として使ってくれます。
マッピングには sqlalchemy.orm.mapperを使い、
とすることでマッピングできます。
sqlalchemy.orm.mapper(クラス名, テーブルインスタンス)
レコードの追加
というわけでhoge_tableのレコードをHogeクラスとして定義し、マッピング。
その後、てきとうなデータを3つインサートし、コミットしてみます。
マッピングするクラスはどうやら新スタイルクラスである必要があるみたいです。
import sqlalchemy.orm
from mytables import hoge_tableengine = sqlalchemy.create_engine('mysql://user:password@localhost/hoge', echo=True)
Session = sqlalchemy.orm.sessionmaker(bind=engine)
sess = Session()class Hoge(object):
def __init__(self, name, level=None):
self.name = name
self.level = levelsqlalchemy.orm.mapper(Hoge, hoge_table)
hoge1 = Hoge('ジャン')
sess.add(hoge1)hoge2 = Hoge('ポール', 3)
sess.add(hoge2)hoge3 = Hoge('ベルモント', 5)
sess.add(hoge3)sess.commit()
これを実行すると、
と標準出力にアウトプットされます。
2009-02-01 05:13:04,062 INFO sqlalchemy.engine.base.Engine.0x...37f0 BEGIN
2009-02-01 05:13:04,078 INFO sqlalchemy.engine.base.Engine.0x...37f0 INSERT INTO hoges (name) VALUES (%s)
2009-02-01 05:13:04,092 INFO sqlalchemy.engine.base.Engine.0x...37f0 ['\xe3\x82\xb8\xe3\x83\xa3\xe3\x83\xb3']
2009-02-01 05:13:04,171 INFO sqlalchemy.engine.base.Engine.0x...37f0 INSERT INTO hoges (name, level) VALUES (%s, %s)
2009-02-01 05:13:04,171 INFO sqlalchemy.engine.base.Engine.0x...37f0 ['\xe3\x83\x9d\xe3\x83\xbc\xe3\x83\xab', 3]
2009-02-01 05:13:04,171 INFO sqlalchemy.engine.base.Engine.0x...37f0 INSERT INTO hoges (name, level) VALUES (%s, %s)
2009-02-01 05:13:04,171 INFO sqlalchemy.engine.base.Engine.0x...37f0 ['\xe3\x83\x99\xe3\x83\xab\xe3\x83\xa2\xe3\x83\xb3\xe3\x83\x88', 5]
2009-02-01 05:13:04,187 INFO sqlalchemy.engine.base.Engine.0x...37f0 COMMIT
あら?と思うかもしれませんがテーブルのエンジンはMyISAMでも、commitしないとデータが反映されないみたい。commit()したときにSQL発行している模様です。
複数レコードを同時に投入する場合は
という感じでリストで渡した方がスマートです。
sess.add_all([hoge1, hoge2, hoge3])
PyYAML 3.0.8
いろんなフレームワークで設定ファイルなどに採用されているので、PythonでYAMLを読んでみる。
おそらくメジャーどころのYAML用モジュールとしてはSyckを利用するPySyckもありますが、PyYAMLというのもあります。
ちなみに実際にインポートするパッケージ名は
- PyYAMLだとyaml
- PySyckだとsyck
です。
両方ともload()とdump()というかたちで入出力を実装しているので、扱い方はほとんど一緒かなと。
とりあえず読む
load()の第一引数にYAML形式の文字列か、file型を読み込めます。
ただしfileの場合は
のエンコードで記述してある必要があるとのこと。
こうしてファイルから読み込んだ場合、マルチバイト文字列はUnicodeになります。
オプションですがキーワード引数で
とすればYAMLのパースにCで書かれたLibYAMLを利用するとのことなので、デフォルトのパーサの実装まで見てませんがそれがPurePythonなら、速度が向上すると思われます。
yaml.load(file, Loader=yaml.CLoader)
単純な例だとこんな感じ。
>>> import yaml
>>> src = yaml.load('hoge: [ [1,2,3], [a,b,c], {foo: FOO,bar: BAR}]')
>>> type(src)
>>> src
{'hoge': [[1, 2, 3], ['a', 'b', 'c'], {'foo': 'FOO', 'bar': 'BAR'}]}
書き出す
dump関数で出力できます。
単純な例だとこんな感じ。
タプルもシリアライズされて出力されてます。オブジェクトもシリアライズしてくれるそうです。あんまり使う機会はないと思いますが。
>>> import yaml
>>> src = {'foo': [1,2,3], 'bar': {'a': 'aaa', 'b': 'bbb'}, 'baz': (11, 22)}
>>> yaml.dump(src)
'bar: {a: aaa, b: bbb}\nbaz: !!python/tuple [11, 22]\nfoo: [1, 2, 3]\n'
ちなみに第2引数にファイル型をとると、そいつに出力してくれます。
ただ、マルチバイト文字列がある場合には
こういうコードを実行すると、
# -*- coding: utf-8 -*-
import yaml
モロにこういう内容が書き出されるわけで・・・、多分別の実装からでは読み込めないんじゃないかなーと。
["\u65E5\u672C\u8A9E"][!!python/str "\u65E5\u672C\u8A9E"]
YAMLをプログラムから書き出すなんてあまりすることはないと思いますが、もうちょっとスマートなやり方とかないんでしょうかね。
Colubrid 0.10
PythonといえばWSGI、WSGIといえばPython。
というわけで、シンプルなWSGIパブリッシャーColubridでWSGIの体験です。
コードも短くてシンプルなのでサクッと読めてしまいます(WSGIミドルウェアが提供してくれる引数が得体が知れないのでそこら辺若干の読みづらさはあるかもですが)。
あと、WSGIについては、ちょっと長いですが
を見ればどういったものかわかると思います。
PyPIのcolubridのページにHelloWorldのサンプルがあるんですが、そのソースだけではどう動かしてよいのやら?な感じで、また、開発元のドキュメントにあるサンプルソースもちょっと古いのか?うまく動かないっぽいです。
で、動くHelloWorldはこんな感じに。
from colubrid import BaseApplication, HttpResponse
from colubrid import executeclass HelloWorld(BaseApplication):
def process_request(self):
return HttpResponse('Hello World!')app = HelloWorld
if __name__ == '__main__':
execute()
このソースを実行すると、8080番ポートでhttpdが起動しますんで、ブラウザにアクセスすれば無事『Hello World!』が表示されるわけです。
このソース自体のメインの処理はexecute()関数ですが、これの本体は
colubrid.server.execute
でして、colubrid/__init__.pyで
from colubrid.server import execute
としてあるので、server.pyのexecute関数が実行されているわけですね。
server.pyのexecute()関数ではWebサーバモジュールを読み込んで実行しているんですが、
- pasteのhttpserver
- BaseWSGIServer
- wsgiref
を上から探して、importできたものをWebサーバとして使ってくれます。
execute()関数に与える引数は
- app WSGIでいうところのcallableなアプリケーション。デフォルトはNone
- debug デフォルトでTrue
- hostname 見てのとおりホスト名。デフォルトで'localhost'
- port ポート番号。デフォルトで8080
- reload リロードフラグ、デフォルトでFalse
- evalex デフォルトでFalse
となってまして、基本的にapp以外デフォルト値が決まってるんでさっきのHelloWorldではexecute()関数に何も渡しませんでしたが動作したわけです。
一番下の二つは本筋から離れるので気にしない方向で。ていうかそこまで追っかけてみてません。。
appについては
app = frm.f_globals['app']
という具合でexecute()の呼び元から拾ってきてくれます。
つまりはHelloWorldクラスがappとして利用されるわけです。*1
ここで冒頭で紹介したPEP333に戻るわけですが、アプリケーションオブジェクトの定義について
アプリケーションオブジェクトは 2つの固定引数を受け付けなければならない。例証のために、それらを environ と start_response と名付けるが、これらの名前を持つ必要はない。サーバまたはゲートウェイは、固定引数 (キーワード引数ではなく) を使用してアプリケーションオブジェクトを呼び出さ なければならない。 (例えば、上に示されるように、result = application(environ, start_response) のようにして呼び出す)
とあります。
HelloWorldクラスの親クラスであるBaseApplicationクラスは、colubridのapplicationモジュールで定義されていて、そのコンストラクタは
def __init__(self, environ, start_response, request_class=Request):
なってます。変数名がPEPと同じ表現なので一目でわかりますね。
また、アプリケーションオブジェクトについて追加で
アプリケーションオブジェクトは、サーバによって呼ばれると、文字列を yield する iterable を返さなければならない。
とあります。
BaseApplicationクラスは
def __iter__(self):
メソッドが宣言されているため、この条件も満たす、というわけです。
単純な例でしたが、とどのつまり
WSGIに乗っかるフレームワーク(もしくはアプリケーション)側は最低限これだけ満たせば良いってことが実際のソースレベルでわかったつもりになれるかなと思います(´・ω・`)
CleverCSS 0.1
何にするか物色するだけで2時間掛かってしまったので、Pythonらしくかつ簡単なのをチョイス。
紹介文の中に
inspired by Python
とあるように、{}を使わずにインデントでスタイルシートを表現して、それを通常の形式にコンバートしてくれるやつです。
インストールはeasy_installで一発でした。
easy_install clevercss
インデント形式なので、ぱっと見YAMLっぽい印象。同じ記述を何度も書く必要がないので、タイプ量は減らせるようです。
問題はデザイナーさんがこれを使うかというとこかとおもいますが(´ω`)
英文を読まなくても簡単な例が書いてあるのでそれを見ると一目瞭然かと思います。
Pythonのパッケージらしく、モジュールとして利用する方法とコマンドラインから利用する方法、両方使えます。
- モジュールとして使う場合
import clevercss
before = '''
#インデントの形式で記述したCSS
'''
print clevercss.convert(before)
- コマンドラインから
python clevercss.py 元ファイル
てな具合です。