est! est! est!な日記
どう書くorgのテストに30分もかかってしまう事態を何とかしたいのだけど、 そもそも30分かかるのが当たり前のことなのか異常な事態なのかそれがわからない。
= 最近ライブドアリーダーを使い始めたのだけど、 Spaceキーを押しても1ページスクロールしてくれないのに困って、 ソースコードを読んでみた。 reader_main.jsの2005行目:
scroll_page: function(num){
var h = $("right_container").offsetHeight - 40;
var c =
(Config.scroll_type == "page") ? h:
(Config.scroll_type == "half") ? h / 2 :
(Config.scroll_px || 100);
$("right_container").scrollTop += c * num;
},
1ページスクロールのオプションがあるみたいだなぁ。
と思って設定画面に行ったら詳細設定のところにスクロール量の設定があった。
なんか順番間違ってる気がした。
= ノートパソコンで30分かかったテストはラボマシンに移動してやったら2分かからなかったorz
リンク切れテストまでかけても3分程度だ。
= 日記あまり書いてなかった。 views.pyを切り分けた。 今までのコードがほとんど__init__.pyに入っていて、 コメント投稿部分だけ別のファイルに切り出した。 いままでmeadowでC-x C-f vi[TAB]と押してビューを出していたのに、 それじゃダメになった。面倒だぞ。
モデルのリファクタリング方法に関して、 テーブルの名前が変わってしまうのでmodelsを 複数のファイルに分割してimportするという手は使えない。 (自分でテーブルの名前を変更して回るのなら使える)
で、よく考えたらテーブルの定義と、ユーティリティ関数が同じところにある必要はないので models.pyはテーブルの定義を入れるところと見なし、 ユーティリティを別ファイルに切り出す形で整理することにした。
いま、例えばUserオブジェクトのabsolute_urlは下のように 「後からメソッド挿入」で実装されている。 こういうのを別のファイルに切り出すと言うこと。
def get_absolute_url(self):
return '%s/user/%s/' % (settings.URL_BASE, self.id)
User.get_absolute_url = get_absolute_url
Commentみたいな自分で定義したクラスは、
普通にクラスのメソッドとして実装されているけども、
よく考えるとフィールドの定義と動作の定義が同じ場所にあるのも不自然。
= 思うんだけども、 どうやら僕はPythonスクリプトが大規模になったときにどう切り分けるかというノウハウが足りないような気がする。 クラスの定義と、そのクラスに動的にメソッドをつっこむところを分けたい。 そしてパスとかややこしくなるのが怖いのでexecfileじゃなくてimportで何とかしたい。
class Foo:
pass
Foo.x = 1
を
class Foo:
pass
と
Foo.x = 1に分けたい。 そしてフレームワークの都合で呼び出されるのはクラス定義のある側なので、 メソッド追加側は何らかの方法でFooへの参照を獲得する必要がある。 しかしここでimportとかするわけにはいかない(よね?)
根本の側から参照を渡してやる。
class Foo:
pass
from bar import wrap
wrap(Foo)
これで原則的にはできる。
で、問題は複数のクラスがあっておたがいに参照しているケースなのだけど、
それはglobals()を渡して必要なものだけ取ってもらえばOK。
これでいいのかなぁ。 いっそglobals().update(given_globals)でもOKだが…。
= 投稿フォームに?parent=parent_idって感じでGETで親コメントのIDを渡していたが、 フォームのactionにもそれを入れておかないとフォームのバリデーションエラーで やり直しになったときにparentが失われてしまう。
= 土曜日。 結局今週もちっとも原稿が進まなかったので、 秋葉原の喫茶ルノワールに来ています。
= models.pyのリファクタリングの話の続き。 models.pyはモデルの定義なのだから、 models.pyの例えばCommentの定義に、 モデルの他の要素、たとえばRatingの関係する処理を書くのはおかしい。 その思想には同意できる。
しかし現実的な問題として 「コメントに付いているレーティングのうち値が0でないものの 値の合計を、その個数の合計でわった値を求めたい」 というときに、そのコードをどこに書くべきか。
テンプレートであまり複雑な処理をすべきではないし、 そもそもテンプレートでは書けないような処理もある。 テンプレートで無理矢理やるために、それに特化したテンプレートタグを作るのもおかしい。
ではビューでやるのか。 この場合問題になるのは、 テンプレートの中でどういう形でCommentが表示されるかビューにはわからないところだ。 ビューであらかじめ計算した結果とCommentオブジェクトと結びつける方法がない。
単にforで順番に表示などで事前にわかる場合は コメントのリストと計算値のリストを渡すとか、 コメントと計算値を辞書でくるんでリストを作って渡すとかで なんとかなるかも知れないが、 regroupやテンプレートの再帰呼び出しが使われる場合はそうも行かない。 本来MVCのVが担当すべき「どういう順序で表示するか」という情報まで、 全部Cで頑張ってしまうハメになる。 これはおかしい。
結局それはテンプレートが関数を明示しないでも評価してしまうところに問題がある。 Ruby的なんだな。 Pythonでxs.uniqと書けば、それはxsというオブジェクトのuniqというメンバで、 それが関数かどうかに関係なくそのオブジェクトが取得できる。 しかしRubyでxs.uniqと書けばこれはxsがArrayの時にはuniqがメソッドなので 勝手に呼ばれてしまう。xsが別のオブジェクトで、 例えばuniqがtrue/falseの値を取るようなものならもちろん呼び出さない。 おかげでRubyではメソッドをオブジェクトとして受け渡しすることが難しいので、 シンボルという文字列のようなものを使う羽目になる。 Djangoのテンプレートもそれと同じで、もしPython的なことをしようと思ったら 文字列で渡すしかない。apply_funcなんてカスタムフィルタを作ってこうやるしかないだろうか→ {{ comment|apply_func:"calc_rating" }}
もしくは{% apply_func "is_user_already_rated" comment user %}か?
しかし、この方法では apply_funcから見える位置に、 どのオブジェクトを引数に取るかと無関係に、 ずらずらと関数が並ぶことになる。
やはり上で書いたような方法で 「モデル定義のmodels.py」の他に 「モデルにまたがる演算を書く場所」を作るのがいいのではないかと そういう結論にしようと思ってつらつら書いてきた。 一引数の関数はレシーバでその引数を渡せるので テンプレートから{{ comment.calc_rating }}と呼べるし。 でも、二引数以上のケースを考えるとそれで解決とも言い難いなぁ。 やっぱり {% apply_func "is_user_already_rated" comment user %} と書けるカスタムタグが必要なのだろうか。
= そんなことを書くためにわざわざ喫茶店に来ているわけではない。 原稿を書かなければ。
= 隣の席の人が自信たっぷりに「ハムニカ構造」とかいうから 「ハニカム構造だっ!」とつっこみたくてしょうがない。
= いま、ファミコン時代の電子音を聞くと 懐かしい感じがして逆に面白いのと同じで、 あと10年くらいしたらMIDIの曲が逆に面白くなるのだろうか。 20世紀のカラオケみたいとか。
= ルノワールの水出しコーヒーはおいしいと思う。
= 壁に向かって作業しているのだけど、 壁に鏡があるので後ろが見える。 後ろの席のおじさんがまじめな顔でエロDVDの裏の説明を読んでる。 帰ってゆっくり読めばいいのに。
家には安息の場所がないのだろうか。
こわいこわい。
= RubyはPythonよりもピュアなオブジェクト指向なのかも知れないが、 世の中の大半の人は奥底がピュアかどうかより外見がそれっぽいかに気を取られる。 Pythonのオブジェクト指向がJavaと比べて「中途半端なオブジェクト指向だ」だなんて、 どこをどう勘違いしたのやら(見かけにだまされているとしか)
詳しく知らないけどオブジェクト指向度はこんな感じ?
Smalltalk >> Ruby > Python >> Java > Perl >> 機械語
= amachangが「数ヶ月前の自分のコードは汚くて捨てたくなる」みたいなことを言っていたけど、 僕のJython本の原稿に出てくるコードはcamelCaseで書かれていて全部修正したくなる。 PEP8読めよ昔の自分!
最近のコードは全部splited_with_underscore。
= 人が減ってきたなぁ
= Pythonってクラスベースなのかな。 それともプロトタイプベースなのかな。 定義って明確にあるものなのかな。
= 色々調べ物がしたいが、 ネットにつなぐためだけに永田町まで行くのもなんかなんなんだな。
はっ、これはマンガ喫茶メソッド?!
いやまて、 マンガ喫茶に行ってもラボに行っても、 まともに眠れないのは同じだ。 選択肢としては 「マンガ喫茶に行って帰れる時間で帰る」と 「ラボに行って帰れる時間で帰る」と 「ラボで泊まる」の三択だ。
たぶん泊まるとぐだぐだになって明日も潰れてダメだろうな。 ここは8時までだから2~3時間マンガ喫茶に行く方針かなぁ。
= はっ。 カプセルイン秋葉原という手があったか! 無線LAN完備で、個室で、入浴設備があって、眠れる。 天井が低くて腰に悪いのが難点だが…。
あっ、前の冬山Pythonキャンプに誰かが持ってきていた座椅子とか買えばパーフェクト?! 誰が持ってきていたのか忘れた…。
とりあえずお腹空いた。 ルノワールは20時までかと思ったけど、そうでもないみたい。 でもお腹空いた。
今日はだいぶはかどった。 具体的に言うと…3キロバイト…がっくり。 たくさん書いた気がしたのに1500文字か。原稿用紙4枚か。 道のりは遠いなぁ…。
冷静に考えよう。 選択肢は「マンガ喫茶に行って2時間で帰る」と 「カプセルインで寝るまで書く」の二択。 カプセルインは3000円だったかな。 マンガ喫茶は8時まで1500円+延長30分500円とかかなぁ。 まったく相場がわかっていない。 とりあえずサーベイに行こう。
ルノワールは総武線と山手線の周りにあるらしい。 錦糸町にはなかったっぽい。
マンガ喫茶はルノワールの上の階にあった。 最初の1時間が400円、2時間で880円、 ナイトパックが6時間980円だったかな。 0時に入って朝の6時に出られるはずがないので、 9時まで居て2200円、11時までで3000円か。 だったらカプセルインといい勝負かも。 カプセルインより座り心地がよくて寝心地が悪い。
= ヨドバシカメラはまだ空いているのか。
ヨドバシカメラの上のカレー屋って「コバラヘッタ」って名前なのか。
カレーを食べて「そういえばルノワールにもYahooBBのマークがあったなぁ」 と1Fのマークを眺めていたら店員にキャッチされる。
今使っているb-mobileが切れたらe-mobileに乗り換えることに決まった。
= カプセルイン秋葉原。4000円。 午後五時から入れて翌朝十時チェックアウト。 がら空き。
エアコンの音がひゅるひゅるする。 消音ヘッドホン持っててよかった。
このマシンは一度ワイヤレスLANに電源が入ったままサスペンドしてしまうと、 再起動するまでつながらなくなる。 よって再起動。
= 再起動によって画面から原稿を開いていたテキストエディタが消えたせいで ぐだぐだに。だめじゃん。
=
>>> function counter(){this.push = function(){this.count += 1;x}; this.count=0}
>>> x = new counter()
Object count=0
>>> x
Object count=0
>>> x.push()
>>> x
Object count=1
できたオブジェクトがcounterへの参照を持っていないのが気になる。
{package Counter;
sub new {
my $pkg = shift;
my $hash = {
count => 0,
name => shift,
};
bless $hash, $pkg;
}
sub push {
my $self = shift;
$self->{count}++;
print $self->{name}, $self->{count}, "\n";
}
}
$counter = Counter->new("car");
$counter->push();
$counter->push();
$counter->push();
ふむ。
Perlの場合、パッケージに複数の関数が入っている、という仕組みがあらかじめあって、 そこに「インスタンスごとの値が入る名前空間(ハッシュ)を貼り付ける」 という方法でオブジェクト的なものをつくったと言うことか。
function counter(name){
this.push = function(){
this.count += 1;
console.log(this.name + this.count)
};
this.count = 0;
this.name = name;
}
>>> x.push() car1 >>> x.push() car2
>>> y = new counter("hoge")
Object count=0 name=hoge
>>> x.push === y.push
false
ふむ。
>>> function Counter(name){
this.count = 0;
this.name = name;
}
Counter.prototype.push = function(){
this.count += 1;
console.log(this.name + this.count)
};
function()
>>> x = new Counter()
Object count=0
>>> y = new Counter("hoge")
Object count=0 name=hoge
>>> x.push === y.push
true
なるほど。
クラスがないからコンストラクタと作られる
オブジェクトを結びつけるものもないのか???
>>> def push(this):
... this["count"] += 1
... print this["name"], this["count"]
...
>>> def Counter(this, name):
... this["count"] = 0
... this["name"] = name
...
>>> Counter.__proto__ = {}
>>> Counter.__proto__["push"] = push
>>> def new(init_func, *args):
... o = {}
... init_func(o, *args)
... o["__proto__"] = init_func.__proto__
... return o
...
>>> new(Counter, "car")
{'name': 'car', '__proto__': {'push': <function push at 10317191>}, 'count': 0}
>>> counter = new(Counter, "car")
>>> def get_attr(o, name):
... v = o.get(name)
... if not v:
... v = get_attr(o["__proto__"], name)
... return v
...
>>> get_attr(counter, "push")(counter)
car 1
>>> get_attr(counter, "push")(counter)
car 2
>>> get_attr(counter, "push")(counter)
car 3
JavaScriptがやっていることをPythonで実装してみた。
ようするにこういうことか。
プロトタイプチェーンがないので、get_attrで実装。
counter.pushが、プロトタイプチェーンのあるget_attrで解決された後、
JavaScriptではレシーバをthisにいれるけど、
Pythonには暗黙のthisがないから直接渡す。
>>> def new(pkg, name):
hash = {"count": 0, "name": name}
return bless(pkg, hash)
>>> def push(self):
get_hash(self)["count"] += 1
print get_hash(self)["name"],
print get_hash(self)["count"]
>>> Counter = {"package":{
"new": new,
"push": push,
}}
>>> def bless(pkg, hash):
return {"package": pkg["package"],
"hash": hash}
>>> def get_hash(obj):
return obj["hash"]
>>> def arrow(pkg, name):
return pkg["package"][name]
>>> counter = arrow(Counter, "new")(Counter, "car")
>>> counter
{'hash': {'count': 0, 'name': 'car'},
'package': {'push': <function push at 0x0165AB70>,
'new': <function new at 0x0165ABB0>}}
>>> arrow(counter, "push")
<function push at 0x0165AB70>
>>> arrow(counter, "push")(counter)
car 1
>>> arrow(counter, "push")(counter)
car 2
>>> arrow(counter, "push")(counter)
car 3
Perlのx->{y}は->と{}の組み合わせではなくて単体の演算子だと判断したけど、
あっているかどうかは不明。
= そういえば書き忘れたけどPythonで書けばこんな感じ。
>>> class Counter():
def __init__(self, name):
self.count = 0
self.name = name
def push(self):
self.count += 1
print self.name, self.count
>>> counter = Counter("car")
>>> counter.push()
car 1
>>> counter.push()
car 2
上のJS版のこの2行は順序が違うな。
プロトタイプの中の値にinit_funcの中でアクセスできるもんな。
... init_func(o, *args) ... o["__proto__"] = init_func.__proto__うんやっぱり。下のようにして確認できる。
>>> function init_func(){this.hello()}
>>> function hello(){console.log("hello")}
>>> init_func.prototype.hello = hello
hello()
>>> new init_func()
hello
Object
Pythonのクラスをクラスを使わずに実装
>>> def __init__(this, name):
this["count"] = 0
this["name"] = name
>>> def push(this):
this["count"] += 1
print this["name"], this["count"]
>>> Counter = {
"__init__": __init__,
"push": push,
}
>>> def instantiate(class_, *args):
o = {}
o["__proto__"] = class_
class_["__init__"](o, *args)
return o
>>> instantiate(Counter, "car")
{'count': 0, 'name': 'car',
'__proto__': {'push': <function push at 0x01EA94F0>,
'__init__': <function __init__ at 0x01EA9230>}}
ううむ。本質的にJavaScriptと同じような気がしてきた。
JavaScriptの場合は、
「初期化のための関数」が「プロトタイプ」と呼ばれる連想配列を持っていて、
そのプロトタイプの中に他のメソッドが入っている。
Pythonの場合は、
「クラス」が「名前空間」と呼ばれる連想配列を持っていて、
その連想配列の中に初期化のための関数と他のメソッドが入っている。
プロトタイプと呼ばれるものはないのでプロトタイプチェーンもないけども、
当然ながらクラスの名前空間は親へとチェーンしている。
Pythonって本当にクラスベースなんだろうか。 クラスベースとプロトタイプベースの違いって何なんだろうか。
= じゃじゃん。 Jython本の原稿の進捗を発表します! 現在の達成率、66%! ダメじゃん!全然ダメじゃん!月末までに終わらないじゃん! 今まで何やってたんだよ自分! どう書くorgやってる場合じゃないじゃん!
来週からは週に1回ペースで3週間発表がある。 ううむ。
= 段ボールで作った魚眼レンズアタッチメントVer.0.01は、 光軸が安定しないのでもっときっちり支えられるように紙粘土を買ってきた。 余った紙粘土は一応袋に入れてくくったけど、 どうせ次に使うときには乾いてしまっているからと思って、 何か作ってみることにした。
とりあえず胴体部分だけの塑像を作ってみた。 トルソーっていうのかな。 おおまかな曲線だけで人間を表現。 初めてにしてはまぁ悪くない感じにできた。 紙粘土だから表面にでこぼこ感があるのが残念だなぁ。 これをシリコンで型どりして硬質レジンでも流し込んで、 頑張って磨いたらきれいななモノができるかなぁ。
= 攻殻機動隊面白いなぁ。 終わった後のインタビューがさらに面白い。 今日はドリー&ズームという用語を覚えた。 ヒッチコックがよく使っていたらしい。
フィードバック
冬に座椅子持って行ったのは自分かなあ。
アウトドア座椅子特集 http://plaza.rakuten.co.jp/kisaraneko/2000
>クラスベースとプロトタイプベースの違いって何なんだろうか
興味深い話です。 概念的にはclass baseよりprototype baseのほうが広くて、prototype baseに対して「classからしかobjectを生成できない」という制限を加えたものがclass baseではないでしょうか。 PythonのOOPはこれで説明できそうな気がします。 ただRubyのように変数と関数(メソッド)の名前空間が異なる場合はまた違ってくるのでしょう。