今日、 Facebook iPhone アプリの作者 Joe Hewitt の発言がtwitterに流れた。
joehewitt: I think for a while there I was actually convinced that I liked Objective-C. (Objective-C が好きとおもったんだけどな)
markhammonds: @ joehewitt What changed your mind? (何で気変わったの?)
今日、 Facebook iPhone アプリの作者 Joe Hewitt の発言がtwitterに流れた。
joehewitt: I think for a while there I was actually convinced that I liked Objective-C. (Objective-C が好きとおもったんだけどな)
markhammonds: @ joehewitt What changed your mind? (何で気変わったの?)
最近、会社の AE35 さんが growltestrunner を作っていて、 modipyd を使って、ファイルを更新したタイミングで自動テストを自動的に実行してくれて、growlで通知するように素敵な環境を設定した。
俺はlinuxなので、当然 growl がないけど、Mac OS の growl みないな libnotify があって、pynotify と言うpythonバインディングがあるから、pynotifyでも使えるように、 fork を作った。
ちなみに、pynotify はこういう使い方
import pynotify
pynotify.init("My App")
n = pynotify.Notification("Title", "Message", "/path/to/my/icon.png")
n.show()
BPStudy #29 のテスト駆動開発の話でペアプログラミングで、Last Recently Used キャッシュ (LRU)を自動テストやりながら、実装しようという部分がありました。
最初に僕は二つのリストで10分くらいで実装したんですけど、やっぱりパフォーマンスが出ないと思ったから、時間が終わったまでに、pythonの辞書で書き直した。
最終版はこれでした。
lru.py
#:coding=utf8:
class LRU(object):
def __init__(self, size=2):
self.size = size
self.name_list = []
self.value_dict = {}
def put(self, name, value):
if name not in self:
self.name_list.append(name)
self.value_dict[name] = value
if len(self.name_list) > self.size:
old_name = self.name_list[0]
del self.name_list[0]
self.value_dict.pop(old_name)
def get(self, name):
if name in self.name_list:
self.name_list.remove(name)
self.name_list.append(name)
return self.value_dict[name]
else:
return None
def __contains__(self, key):
try:
self.value_dict[key]
return True
except KeyError:
return False
def __setitem__(self, key, value):
self.put(key, value)
def __getitem__(self, key):
return self.get(key)
def __len__(self):
return len(self.name_list)
lru_test.py
from unittest import TestCase
from lru import LRU
class LRUTestCase(TestCase):
def test_put(self):
lru = LRU()
lru.put("monjudoh", "monju")
self.assertEquals(lru.get("monjudoh"), "monju")
def test_multi_put(self):
lru = LRU()
lru.put("monjudoh", "monju")
self.assertEquals(lru.get("monjudoh"), "monju")
lru.put("monjudoh", "monju2")
self.assertEquals(lru.get("monjudoh"), "monju2")
self.assertEquals(len(lru), 1)
def test_loss(self):
lru = LRU()
lru.put("monjudoh", "monju1")
lru.put("ian", "monju2")
lru.put("test", "monju3")
self.assertEquals(lru.get("monjudoh"), None)
def test_use(self):
lru = LRU()
lru.put("monjudoh", "monju1")
lru.put("ian", "monju2")
self.assertEquals(lru.get("monjudoh"), "monju1")
lru.put("test", "monju3")
self.assertEquals(lru.get("ian"), None)
self.assertEquals(lru.get("monjudoh"), "monju1")
def test_size(self):
lru = LRU(size=4)
lru.put("monjudoh", "monju1")
self.assertEquals(len(lru), 1)
lru.put("ian", "monju2")
self.assertEquals(len(lru), 2)
lru.put("test", "monju3")
self.assertEquals(len(lru), 3)
lru.put("test2", "monju4")
self.assertEquals(len(lru), 4)
lru.put("test3", "monju5")
self.assertEquals(len(lru), 4)
lru.put("test5", "monju6")
def test_bigger(self):
lru = LRU(size=4)
lru.put("monjudoh", "monju1")
lru.put("ian", "monju2")
lru.put("test", "monju3")
self.assertEquals(lru.get("monjudoh"), "monju1")
lru.put("test2", "monju4")
lru.put("test3", "monju5")
self.assertEquals(lru.get("ian"), None)
self.assertEquals(lru.get("test"), "monju3")
self.assertEquals(lru.get("test2"),"monju4")
self.assertEquals(lru.get("test3"),"monju5")
夏のPython温泉でBob様が作ってくれたピュアーパイソンクライアントpytyrantは酒徳さんのpython-tokyotyrantより速いという話を聴いたとmoriyoshiさんに言った。それで、moriyoshiさんはprofileのテストを作ってくれたけど、結果として、pytyrantとpython-tokyotyrantはあまり変わらないのが出た。
でも、このコードは一つのスレッドでテストしている。複数のクライアントが同時に接続している場合はどうかと思って、今日テストを作ってみた。
これを実行するとpytyrantのほうがずっと速い.
python-tokyotyrant
********************
Running 20 threads took 6.755 seconds
Running 20 threads took 5.392 seconds
Running 20 threads took 5.516 seconds
Running 20 threads took 27.191 seconds
Running 20 threads took 30.575 seconds
Running 20 threads took 34.699 seconds
pytyrant
********************
Running 20 threads took 1.748 seconds
Running 20 threads took 1.736 seconds
Running 20 threads took 1.716 seconds
Running 20 threads took 8.922 seconds
Running 20 threads took 8.716 seconds
Running 20 threads took 8.746 seconds
あるプロジェクトの依存は特定なバージョンじゃないとダメな場合が結構多いと思いますけど、最近、pythonの仮想環境を簡単に作れるようになりました。virtualenv と virtualenvwrapper と pip の組み合わせを紹介します。
virtualenvは環境を作ってくれるライブラリで、virtualenvwrapperはその作った環境を簡単に管理してくれるツールになります。
easy_install virtualenv
easy_install virtualenvwrapper
これで、virtualenvwrapperのコマンドを使うには、bashスクリプトを設定しないといけない。以下の行を .bashrc に追加
export WORKON_HOME=$HOME/.virtualenvs
source /usr/local/bin/virtualenvwrapper_bashrc
コンソールを再起動して終わり。仮想環境を作りましょう。
mkvirtualenv myproj
これで、プロジェクトの仮想環境ができました。 これで、pythonのバージョンも確定し、仮想環境にインストールするライブラリのバージョンも確定になる。mkvirtualenvを実行すると、作った環境に入る。また、次に使う時は workon myproj を実行して、環境に入る。
しかし、よく使われてるeasy_install は virtualenv と連携できなくて、どうしても、システムのpythonディレクトリにパッケージをインストールしてしまう。
easy_install が簡単すぎて、分けわかんないメッセージも出したりして、virtualenvと相性悪くて、何とかできませんか?って話があったきかっけ、pipが生まれた。pipをインストールすればvirtualenvにすいすいとパッケージをインストールできる。virtualenvを使っている間にインストールする。
wget http://pypi.python.org/packages/source/p/pip/pip-0.4.tar.gz
tar xzf pip-0.4.tar.gz
cd pip-0.4
python setup.py install
Update: pipはちゃんと、virtualenvに入ってくれるので、easy_install でもインストールできる。
workon myproject
easy_install pip
これで、pip が仮想環境にインストールする。これから、pip install で仮想環境にパッケージをインストールできるようになった。それで、プロジェクトに必要なパッケージをインストールし、pythonのsite-packagesが汚くならないので、安心
Python温泉に参加しに行ってきました。今回はいろいろ話が上がりましたけど、特に未来的な話が一番面白かったのかなと思います。ウェブなど日本なサービスがどうなるのかが特に気になります。アメリカでは、クラウドサービスやマイクロペイメントサービスが色々栄えそうだが、日本では頭が固いから、そのまま栄えない可能性が高いので、オリジナルサービスやアイディアを考えないと行けない。多言語化がやっぱり複数の言語をサポートすることのみではなくて、文化や地元の事情を考えながら、地元の機能を作ること。
あと、 酒徳 さんが作ったtokyotyrant ライブラリのC バインディング python-tokyotyrant は Bob が作ったピュアPythonクライアント実装 pytyrant に意外と負けるという話が上がって、 moriyoshi さんがどうしても信じなくて、 プロファイリングテスト を作ってくれた。やっぱりローカルでCバインディングのほうが早い。ネットを通るとどうなるのが気になりますけど。
僕の作ったものは 前と書いた通り 、 disqus のAPIの Python クライアント を作って見ました。アップデート実装がまだ出来ていないので、ご注意。
明日からPython温泉に参加しに行ってきます。課題は下記のとおり
どこまでできるかわかんないけど、たぶんあまり眠らないでしょう。
Pythonの例外オブジェクトは苦手です。例外のメッセージが何でもasciiとして扱われることがひどい。
| In [1]: t = ValueError("テスト".decode("utf8")) | |
| In [2]: print t | |
| --------------------------------------------------------------------------- | |
| UnicodeEncodeError Traceback (most recent call last) | |
| /home/ian/src/project/<ipython console> in <module>() | |
| UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128) | |
| In [3]: t = ValueError(u"テスト") # Unicode object's don't work either | |
| In [4]: print t | |
| --------------------------------------------------------------------------- | |
| UnicodeEncodeError Traceback (most recent call last) | |
| /home/ian/src/project/<ipython console> in <module>() | |
| UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128) |
Djangoのテストフレームワークで、doctestを使おうと思ったら、例外のメッセージがasciiじゃないとダメというのが判明
| u""" | |
| >>> test(-1) | |
| Traceback (most recent call last): | |
| ... | |
| ValueError: エラーですよ! | |
| """ |
とやっても、うまくうごかない。以下のUnicodeDecodeErrorがでる
======================================================================
ERROR: Doctest: app.tests
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/ian/.virtualenvs/test/lib/python2.5/site-packages/django/test/_doctest.py", line 2175, in runTest
test, out=new.write, clear_globs=False)
File "/home/ian/.virtualenvs/test/lib/python2.5/site-packages/django/test/_doctest.py", line 1403, in run
return self.__run(test, compileflags, out)
File "/home/ian/.virtualenvs/test/lib/python2.5/site-packages/django/test/_doctest.py", line 1291, in __run
got += _exception_traceback(exc_info)
File "/home/ian/.virtualenvs/test/lib/python2.5/site-packages/django/test/_doctest.py", line 269, in _exception_traceback
return excout.getvalue()
File "/usr/lib/python2.5/StringIO.py", line 270, in getvalue
self.buf += ''.join(self.buflist)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe3 in position 24: ordinal not in range(128)
----------------------------------------------------------------------
Update: moriyoshiさんにより、Python 2.6 でちゃんと動くみたい
GAE Hackathon Disc. 3 に参加してきました! 私とid:a2c (Twitter) がGoogle Appengineの日本語が対応する全文検索エンジンを作ってみました。
GAE では、データストアが Entity と言う概念で作られてるけど、Entityを検索する時に、データを完全一致しないと、データを取れないので、全文検索が難しくて、以下の状況になってる。
a2cさんが以前に、いろいろ調べたり、試してみたりしてくれたので、いろいろ助かった。いい情報を取れたので、すごくいい話が当日にできました。
SearchableModelのAPIは基本的にいいと思ったので、SearchableModelと同じように、日本語対応できるSearchableModelを使いたいなと思いました。こう書けば、わりと簡単に検索できる。
| from google.appengine.ext.search import SearchableModel | |
| from google.appengine.ext.db import * | |
| class Document(SearchableModel): | |
| title = StringProperty(u"Title") | |
| text = TextProperty(u"Body Text") |
| ... | |
| Document.all().search(keyword) | |
| ... |
まず、SearchableModelがいろいろ、自分を参照したので、継承するのが難しかったから、別のモジュールにコピーして、forkすることにした。それで、この辺に単語の分け方を a2cが作ってくれた ngram実装に切り替えた。(ngramとは?) それで、SearchableModelのモジュールを変えるだけで、googleの実装と同じように使える。
検索キーワードをngramで分けて、インデクスを検索すると、ちゃんと部分的にデータが引っ張ってくる。 http://saichugen.appspot.com/ でテストアプリを見れる。コードはbitbucketで公開されてる。
これから、a2cさんともっと検索結果を取れるようにするのと、インデクスの容量をへらしたりするのと、分け方を自分で実装できるapiを導入したいと思うので、ぜひ期待してください ![]()
ほぼ汎用イベント管理ツールの改善をしようと思ってて、実際にコードを見るとURLを使ってるのが気になった。
WerkzeugのURLルーティングでDjangoのreverse関数みたいにURLの名前からURLに変換できるのかなと調べて、ある方法がありました。名前からじゃなくて、endpointから変換するけど。。。
| from werkzeug redirect as wredirect | |
| from urls import url_map | |
| def reverse(**kwargs): | |
| c = url_map.bind('') | |
| return wredirect(c.build(**kwargs)) | |
| ... | |
| return reverse('form', dict(key=key, slug=slug)) | |
| ... |