« 2007年05月 | メイン | 2007年07月 »

2007年06月25日

Django7日目日記

多対多のリレーションを表現しているフィールド
    tags = models.ManyToManyField(Tag)
に追加をするには
    self.tags.add(tag)
でいい。 ただし、データベース上はself.idとtag.idの対をテーブルに追加するわけなので、 もちろんself.idが値を持っていなければ行けない。 だからこうした。
if not self.id:
    self.save()

= 書き忘れてたけどpygmentsで行番号がずれるのは 標準で付いてくるスタイルシートに行番号のレイアウトに関する記述がないせいで、 行番号の表示に使っているpreのマージンが0ではないせい。
.linenos pre {
  margin: 0pt;
}

= 学会費の請求が。
       請求額          :       12,540円
       入金額          :        4,800円
       不足額          :       -7,740円
しまった。社会人になったのに学生料金で払ったか。 請求書が届いているはずなので探さねば。
= フィードの出力、簡単だった。

引数を取るフィード出力がうまく動かない。 「Invalid feed parameters. Slug 'forlang' is valid, but other parameters, or lack thereof, are not.」 って言われてトレースバックとかが出ない。

Djangoのコードの中からエラーメッセージで検索してみた。 django.contrib.syndication.viewsの中でエラーが出ている。 print文を足してみる。

    try:
        feedgen = f(slug, request.path).get_feed(param)
    except feeds.FeedDoesNotExist:
        raise Http404, "Invalid feed parameters. Slug %r is valid, but other parameters, or lack thereof, are not." % slug
Feedクラスのインスタンスを作ってからget_feedにparamを渡す。 paramは'python'だった。

get_feedの中身:

if url:
    try:
        obj = self.get_object(url.split('/'))
    except (AttributeError, ObjectDoesNotExist):
        raise FeedDoesNotExist
else:
    obj = None
get_objectがどこで定義されているのかわからない。 検索したけど見つからない。tryを外してみたら 「AttributeError at /doukaku/feeds/forlang/python/ 'ForLang' object has no attribute 'get_object'」

ドキュメント(配信フィードフレームワーク : Django オンラインドキュメント和訳)には 「get() は指定された bit を使って適切な巡回区域を選択する役割を担っています.」 と書いてあったのでgetで実装したけど、 そこに載っているサンプルコードはget_objectになっているなぁ。


= Pythonのコードをいじって、 一つのプロセスの中でN個のインタプリタを起動するようにすれば、 Nコア対応なんじゃないか?という話だけど、 帰ってきて考えると 「なんか片方のインタプリタが使っているオブジェクトをもう片方がGCしちゃったりしないかな」 と思った。

でも、あらかじめCPUの個数にあわせてNが決まっているなら、 「このオブジェクトはインタプリタ間でシェアします」って宣言したオブジェクトの 参照カウンタをN+1くらい増やしておくだけでいいんじゃないかとも思った。

ということをここに書いておくとささださんが「それ○○だからダメ」とつっこんでくれるんじゃないかと期待(ぉ


= 「テスト用に作った、本文が「a」になっているコメントを全部削除」はシェルでこんな感じに実行。
models.Comment.objects.filter(body="a").delete()
らくちん。
= こんなビュー
def lang_index(request):
    langs =  models.Lang.objects.all()
    return render_to_response(
        'doukaku/lang_index.html',
        dict(
          langs=langs)) 
をこんな使い方している場合、
urlpatterns = patterns('',
    (r'^lang/$', views.lang_index),

(以下略)
汎用ビューを使う方がいい。 下のように書くだけでいい。lang_indexが必要なくなる。
urlpatterns += patterns(
    "django.views.generic.list_detail",
    (r'^lang/$', 'object_list', dict(queryset=models.Lang.objects.all())),

(以下略)
テンプレートの名前をlang_listに変える。別にテンプレートの名前を指定することもできるけど、 今のファイル名にこだわりがあるわけではないのでデフォルトの名前に。 そして中身はこんなのだったので、langをobject_listに変える。
{% extends 'base.html' %}
{% load i18n %}

{% block content %}
<h2>{% trans 'List of languages' %}</h2>
<ul>
{% for lang in langs %} 
<li><a href="{{ lang.absolute_url }}">{{ lang.caption }}</a></li>
{% endfor %}
</ul>
{% endblock %}

これで6行縮む。しかも、表示する対象が増えてきて1ページに全部じゃ多すぎるな、と思ったときも pagenate_by=20と書き足して、URLでpageをキャプチャするようにすればいいだけ。簡単。


= 詳細表示ページもobject_detailを使える。 今までchallengeって変数に入れていたオブジェクトがobjectという抽象的な名前の変数にはいるけども、 テンプレートが長くてchallengeが何度も出てくるとか、 objectという名前ではよくわからなくなりそうだと思うのであれば {% with object as challenge %}~{% endwith %}で囲ってやるだけ。
= 珍しい、今日はまだ8時になっていないのにみんな帰っちゃった。 amachangがお休みなのもあるのかな。
= テンプレートに「一番根っこのURL」を渡すために、 いちいちrender_to_responseに RequestContext(request)を渡すのが面倒だ(DRYじゃない)って思ったので別の方法を模索して、 テンプレートのincludeで文字列を埋め込むという方法を使ったけども、 結局その方法ではテンプレートで静的な文字列を埋め込んでいるだけなので、 request.userのような変化するものは埋め込めないし、 モデルがabsolute_urlを返すときの文字列も変わるように setting.pyに重複した設定が必要になるし、 ダメダメだった。 素直に全てのrender_to_responseでRequestContextを渡す方がよっぽどスマートだと思った。

そういうわけでこんな感じの関数をviews.pyの冒頭に定義して、 render_to_responseが常にRequestContextを渡すようにしてみた。

def render_to_response(*args, **kw):
    from django.shortcuts import render_to_response
    request = sys._getframe(1).f_locals.get("request")
    kw["context_instance"] = RequestContext(request)
    return render_to_response(*args, **kw)
(_getframeは黒魔術なのでよいこは真似しないでね!)
= あわせて読みたい

URL入れるところに自分のブログのURLを入れて 「※このような画像が表示されます(横160x縦140)」「※画像は数時間ごとに自動的に更新されます。」 「ただいま集計中」 と表示されたので、そのページをブックマークして「いつになったら集計終わるかなー」 と何度か見に行ったけども、1週間経っても集計が終わらずorz。

っていうかブログに貼らなきゃダメだったのね?今気がついたよ!


= 実装は持ち運べるノートパソコンで。 でも簡単にサーバに入れたい。 ファイルをコピーするたびに設定を書き換えるのは面倒だけど、 環境によって分けないといけないファイルを覚えておくのも面倒。

とりあえずURLに関してはこうやってみた。

import sys
is_local = "nt" in sys.builtin_module_names
if is_local:
    URL_BASE = "http://localhost:8000/doukaku"
else:
    URL_BASE = "http://xxxxx.com/doukaku"
そうか、サーバに載せるなら当然DEBUGもFalseにしなきゃいけないな。
import sys
is_local = "nt" in sys.builtin_module_names
DEBUG = is_local
TEMPLATE_DEBUG = DEBUG
is_localなんか作らなくてもDEBUGでよかったんじゃないかという話。

追記:ローカルでは動くのにサーバでは動かない問題を解決するためにDEBUGをONにした。


= 9日目

あんまり長い文章をblocktransで囲むのもどうかと思ったので、 {% include 'welcome.txt' %}みたいな感じでウェルカムメッセージをインクルードすることにした。 さて。 訪問者のロケールによってインクルードするテンプレートを変更するにはどうしたらいいだろうか。


= models.pyのモデル定義にget_absolute_urlを書くのはおかしい気がしてきた。 urls.pyとまとめられないか。
# models.py
    def get_absolute_url(self):
        return '%s/lang/%s' % (settings.URL_BASE, self.slug)
# urls.py
    (r'^lang/(?P<lang_slug>.*)/$',
     views.lang_detail),
うーん。 まぁいいや。
= uniqueなのに
class UserProfile(models.Model):
    user = models.ForeignKey(User, unique=True)
どうしてuserprofile_setになってるんだ?
In [141]: c.author.userprofile_set.all()
Out[141]: [<UserProfile: nishio>]
よくわからないけど、このままでは不便なのでUserクラスにメソッドをつっこんでみた。
def profile(self):
    ups = self.userprofile_set.all()
    if len(ups) > 0:
        return ups[0]
    return None
User.profile = profile
Userクラスは自分が定義したものではないクラスだけど、 中身を気にせずにユーティリティメソッドをつっこむことができて便利。
= models.BooleanField()で宣言されているフィールドに、 フォームでチェックボックスをONにしたときに送られてくる値"on"を入れても、 Trueにはならないのか。

あー、そうか、チェックを外したときには値が送られないから、 そもそも入っている値ではなくてhas_keyでテストした結果を見るべきなのか。


= いつの間にかひらがな入力モードになっているに気づかずに「むむ」と打ったら 「もなもな」って出てなごんだ。
= 「あわせて読みたい」の集計が終わって、表示されるようになっていた。 やっぱり画像を貼らないといけなかったんだね。

あわせて読みたい - あわせて読みたいについて

「あわせて読みたい」では、AMN(あわせて読みたいネットワーク = 「あわせて読みたい」または「フィードメーター」)参加ブロガーの中から、直近約24時間で共通の読者率が高いサイトを、独自の計算により算出した順位で表示しています。

画像パーツを貼り付けた直後は「集計中」と表示されていますが、数時間後から情報の更新がはじまり、約1~2日ほどで安定した集計値が表示されるようになります。

表示は数時間~1日間隔で更新されます。 記事の中で他のブログを紹介するなどすることでも、「あわせて読みたいサイト」は毎日変化すると思います。

なお、順位付けのアルゴリズムは秘密ですが、基本的には共通の読者率が高い(相関性の高い)サイトが紹介されます。(※ Cookie は使っていません)

「Cookie は使っていません」 だそうな。 ということはやっぱり Kazuho Oku's Weblog : 固定化するIPアドレス、そしてブログパーツとプライバシー で書かれているようにIPアドレスで追跡しているのだろうか。
= orz。なんかうまく行かないと思ったらsubmitがsumbitになってた。
= コメントの投稿にプレビュー画面を挟みたい。 コメントオブジェクトを作って表示して、 OKを押したらsaveするってのでいけるかと思ったけども、 プレビューでコメントオブジェクトを作るたびにどんどん書き込まれてしまっている。

プレビューの段階でnew_comment=Comment(a=..., b=...)ってやらないで、 new_comment=dict(a=...,b=...)にしておく。 OKを押した段階でnew_comment=Comment(**new_comment)する。 これが一番エレガントかなぁ?


= モデルに指定する選択肢を、ビューでも使いたい(フォームに)ので こんな風にしてみた。
# models.py

RATING_LIST = [
  ("quality", [_('awful'), _('bad'), _('not bad'), _('good'), _('excellent')]),
  ("speed", [_('slower'), _('slow'), _('normal'), _('quick'), _('quicker')]),
  ("elegance", [_('cruder'), _('crude'), _('normal'), _('polished'), _('elegant')]),
  ("craziness", [_('nuisance'), _('crazy(in bad meaning)'),
                 _('ordinal'), _('crazy(in good meaning)'), _('genius')])
]
class Rating(models.Model):
    comment = models.ForeignKey(Comment)
    user = models.ForeignKey(User)
    date = models.DateTimeField(editable=False)
    for name, choices in RATING_LIST:
        locals()[name] = models.IntegerField(
            choices=zip(range(-2, 3), choices))
# views.py

from doukaku_proj.doukaku.models import RATING_LIST
class RatingForm(forms.Form):
    for name, choices in RATING_LIST:
        locals()[name] = forms.ChoiceField(
            widget=forms.RadioSelect(),
            choices=zip(range(-2, 3), choices),
            label=_(name))

= むむ。コンソールの青い字だけ浮いて見える…。
= ふう。レーティング機能が付いた。 -2~2だと、なんかラジオボタンで値をやりとりするときにおかしくなるようなので 0~4に変えた。
= テンプレートからincludeで取り込んでいる、拡張子がtxtのテンプレートは、 transでマークしてあってもgettextが拾ってくれない気配。

AnonymousUserをstrすると、えらる気配。

Python温泉終了とか日記

朝「みなさんご飯食べてますよ、布団片付けますよ」と宿の人に起こされる →寝ぼけて鍵を持たずに部屋を出る→宿の人が片付けた後鍵を締めていく →みんな締め出された→ごめんなさいごめんなさい

寝ぼけていたので僕が鍵を持ってでなかったことが原因であることに気づくのにかなり時間がかかりました。


= 帰りのブレストでJython本完成への道筋が見えた!

あと色々と面白いアイデア。 詳しいことは、Jythonに関することは本に、それ以外はラボブログに書きます。


= 作ろうと言っていた、お題に対してコードを投稿するサイトは、 いくつかお題を入れて積極的に宣伝はせずに少人数でベータテストできる状態の一歩手前。 まずはDjangoの動くサーバを借りて環境を整え、 メールを使ったアカウント作成がちゃんと動くことを確認します。 ドメインも取ろうかと思ってます。 でもって、「新着お題フィード」を実装します。 それができたらとりあえずベータテスト始めます。 今週中には始められるかな。
= 帰ってきて竹迫さんちでコイルなんとかをみたり。 ビーズクッションは欲しいと思った。 でも帰ってきて自分の部屋を見たら置く場所がなかった。

2007年06月23日

Python温泉2日目

5時に寝て8時半の朝ご飯に起きるのは辛い。 二度寝して11時。
= ネットの調子が悪い。 っていうかLingrの調子が悪いだけか。

開発合宿に必要なもの。 電源タップ。


= スタイルシートのクラスを継承できないかな、という話。 「.comment_bodyも.reply_bodyも.indentを継承」みたいな感じの。

Python Cheese Shop : cssutils 0.9.2a4 を使うとできるか?という話。

jQueryを使ってidでエレメントを取ってclassをつっこんでやるという解決策もあるそうな。


= Pymacs (2006/02/09)
Pymacs framework

Emacs Lisp と Python の相互乗り入れを可能にする仕組みで、elisp に Python のモジュールを読み込んで使えるようにするもの。


= WSGIってなんだろう。 ミドルウェアらしい。
= twitter.py
= LIタグの頭の点だけ色を変えたい。 方法がわからないのでLIのcolorを指定した上で、liの中にdivを入れて 黒に戻した。
= JavaでEclipseとか使っているとCtrl+Clickでソースがすぐに確認できて便利だよね。 Pythonではモジュールの中のソースコードを確認するの面倒だなぁ、と思っていたけど IPythonなら末尾に「??」ってつけるとソースコードが表示される。

「?」一個ではdocstringとかが表示されるけど、「??」にすればそこがコードに変わる。

In [55]: db.reset_queries?
Type:           function
Base Class:     
String Form:    
Namespace:      Interactive
File:           c:\python25\lib\site-packages\django\db\__init__.py
Definition:     db.reset_queries()
Docstring:
    


In [56]: db.reset_queries??
Type:           function
Base Class:     
String Form:    
Namespace:      Interactive
File:           c:\python25\lib\site-packages\django\db\__init__.py
Definition:     db.reset_queries()
Source:
def reset_queries():
    connection.queries = []
一番下の3行。超便利!
= 「コメント本文とコードのどちらかは指定しないといけない」 という制約をフォームに入れるにはどうしたらいいですか。 バリデータを作る必要があるらしい。

バリデータはdjango.core.validatorsにあった。 django.core.validators.[TAB]ってやって一覧を眺めてみると、 RequiredIfOtherFieldNotGivenっていうそれっぽい名前のバリデータがあった。

In [59]: django.core.validators.RequiredIfOtherFieldNotGiven??
Type:           type
Base Class:     
String Form:    
Namespace:      Interactive
File:           c:\python25\lib\site-packages\django\core\validators.py
Source:
class RequiredIfOtherFieldNotGiven(object):
    def __init__(self, other_field_name, error_message=gettext_lazy("Please ente
r something for at least one field.")):
        self.other, self.error_message = other_field_name, error_message
        self.always_test = True

    def __call__(self, field_data, all_data):
        if not all_data.get(self.other, False) and not field_data:
            raise ValidationError, self.error_message
なるほどねー。

「コードだけ、コメント本文だけ、の投稿は許すが、両方空っぽなのはダメ」 っていう制約を記述するのは投稿フォームのHTML(やテンプレート)ではなく、 投稿フォームを作っているビュー(コントローラ)でもなく、 モデルの定義の部分。なるほどなー。 考えてみればものすごく当たり前なんだけども、 その当たり前のことがおろそかになっている。


= 今日やること。 1:コード投稿フォーム、2:auth、3:フィード。 フォームに関してはあとタグとレーティングのをつくんなきゃね。
= youtube-dl: Download videos from YouTube.com。 おおっ、これを使えば特定のタグの付いた動画をごっそり収集とかできるわけか。
= ああ、あと言語の追加フォームもいるか。
= django.utils.encoding.smart_unicode
= 机の半分が「tumblr面白い!」残りの半分が「tumblrって何?」という状況(僕は後者)なので、 とりあえずアカウントを作ってみた。

ふむふむ。


= Django でのユーザ認証 : Django オンラインドキュメント和訳。 tumblrとかLingrとかで気が散って、なかなか読み進められなかった。やっと読んだ。 ユーザー管理はDjangoのauthに最初から入っている。 settingも最初から使う設定になっている。
    return render_to_response(
        'doukaku/comment_form.html',
        dict(form=f, parent=parent, cha=cha,
             user=request.user, loginform=LoginForm()))
ってやる。LoginFormは下のようなクラス。他のはまあ、他の渡したかった値。
class LoginForm(forms.Form):
    signin = forms.BooleanField(label=_("Sign in now?"), required=False)
    username = forms.CharField(max_length=50, required=False)
    password = forms.CharField(max_length=50, required=False,
                                  widget=forms.widgets.PasswordInput())
で、下のようにしたときに、CommentFormに含まれていないsigninとかはスルーされる。
f = CommentForm(request.POST)

= 下のように書いたら「__init__() got multiple values for keyword argument 'required'」って怒られた。
anonymous = forms.BooleanField(_("Sign in now?"), required=False)
大体何が原因か想像が付くけども、IPythonの練習も兼ねて確認。
django.newforms.BooleanField.__init__??
でたソース。(BooleanFieldはFieldの__init__を継承しているのでそっちが出る。偉い!)
def __init__(self, required=True, widget=None, label=None, initial=None
ラベルが一つめの固定引数だと勘違いしていたけども固定引数はないということがわかる。
anonymous = forms.BooleanField(label=_("Sign in now?"), required=False)
これでOK。
= Macはとても熱いので、HappyHackingKeyboardの墨色無刻印を持ち歩く必要がある。
= JUNETは「じぇいゆーねっと」。 JUNET - Wikipedia
= フィードの大半はゴミなのでフィルタリングが欲しいという意見はあるようだけども、 大半がゴミである以上ネガティブなものに印をつけるために労力を消費するようなシステムはダメなのかも。
= 最高のメタデータは人間。

ただし世の中には役に立つメタデータと役に立たないメタデータがある。

だって。

聞き流しメソッド。


= BooleanField(チェックボックス)って、OFFの状態でPOSTするとFalseが来るんじゃなくて何も来ないのか。

バリデーションに関する情報の入ってないフォームだと、 f.clean()で「cleaned_dataという属性はない」ってエラーになっちゃうのか。


= 部屋に戻ったら僕だけネットにつながらなくて悲しい。
= だいぶできた。 えーと。 サイドバーにユーザの名前くらい出したかったのだけど、 それをやるにはリクエストコンテキストをテンプレートの呼び出し時に渡さないといけないのか。
= 明日の朝ご飯はブルースウィリスだってー。

明日の朝ご飯は8時半で、ブルースウィリスの娘が出るらしい。

デミムーアソースってハンバーグにかけるやつ?


= IEで見るとテンプレートが割と期待はずれになるなぁ。

あと僕のブログをOperaで見ると酷いことになることが判明。

2007年06月22日

Python温泉一日目

新橋の駅で迷う。 東海道本線って東京と新大阪を結ぶ新幹線のことだと思ってたよ。 普通の電車なのに途中にグリーン席がある。 普通の席が混んでて座れない。1時間ちょい乗るので作業できないのはもったいない。 1200円。

タッチする自動ドアと、押すドア、引くドア、スライドするドアがあってひどい。

やば、酔ってきた。

二階建てデッキの下へ。


= uにauth.models.Userオブジェクトが入っているとして、 IPythonでprint u.firstnameってやるとエラーになるのはなぜだろうか。
= ダウンロード機能が付いた。 熱海に着いた。バッテリーがなくなった。 やるべきことをリストアップして優先順位をつけよう。
= ついた。
= 会議室はペットボトルの飲み物ならOK。

Mac率が高い、っていうか僕の机は僕以外Macだ。


= 静的略語展開について調べる。 略語展開。 メジャーモード内で登録できるらしい。django-modeで色々登録しよう。 動的展開がM-/なのに対して静的展開は C-x ' (expand-abbrev) で長い。 M-Spaceが「カーソル周囲のスペースをまとめて1個だけにする」 という謎なコマンドに割り当てられていたので これを付け替えることにしよう。

とりあえずdjango-mode内で適当なものを登録してM-x write-abbrev-file。 ファイルネームに何も入れずにEnter。 M-x edit-abbrevsで編集モードに。 あーー、"{ef"で"{% endfor %}"になるようにしようと思ったのに、 記号は略語に含まれないようだ。

とりあえず

(fundamental-mode-abbrev-table)

"eb"	       1    "{% endblock %}"
"ei"	       1    "{% endif %}"
"ef"	       1    "{% endfor %}"
"el"	       1    "{% else %}"

"sb"	       1    "{% block %}"
"sf"	       1    "{% for in %}"
"si"	       1    "{% if %}"

"inc"	       1    "{% include '' %}"

M-x abbrev-modeで略語展開モードにしたらウザイかなと思ったけど、 "ef "で"{% endfor %}"になり、 "def "では展開されない。便利だ。

追記:上みたいに空行開けちゃダメみたい。 そこで途切れるっぽい。 .abbrev_defsを直接編集する方が好みかも。

(define-abbrev-table 'fundamental-mode-abbrev-table '(
    ("ew" "{% endwith %}" nil 1)
    ("tr" "{% trans '' %}" nil 1)
    ("sw" "{% with  as  %}" nil 1)
    ("el" "{% else %}" nil 1)
    ("ei" "{% endif %}" nil 1)
    ("ef" "{% endfor %}" nil 2)
    ("si" "{% if  %}" nil 1)
    ("eb" "{% endblock %}" nil 1)
    ("sf" "{% for  in  %}" nil 1)
    ("sb" "{% block  %}" nil 1)
    ("inc" "{% include '' %}" nil 1)
    ))
write-abbrするとこんな風に適当な順番にされるから、 あくまで最初にちょっと作っておいてあとは自分でこれを書き換える。 もうwrite-abbrしない。
= authに入っているUserを拡張したい(例えば誕生日フィールドをつけるとか)場合、 最初継承してみて、うまくいかないので新しいUserProfileってモデルを作って FKでUserに張ってみた。

あとはそれに「unique=True」しておくのがいいらしい。なるほど。


= テンプレート期待と違う表示だと思ったら challengeって書くべきところがchallangeになってた。 なんかStrictモードっぽいのはないんだろうか。
= Django面白い。 蜘蛛の巣と同じで、 足がかりができてくると動き回るうちにだんだんしっかりしてくる。
= 一対多でコメントがレーティングをたくさんもつとき、 レーティングがコメントにFKする。 コメントは何も宣言しないが、rating_setができてる。

多対多の時は?

ManyToManyを宣言した側にフィールドの名前で普通に入っている。

{% for tag in comment.tags.values %}
  {{ tag.caption }}
{% endfor %}

= {{ tag.caption }}を{{ tag.caption|escape }}にしておかないと、 HTMLタグが書かれたときに通ってしまう。 表示部分でレンダリングのたびにエスケープするんじゃなくて、 入力されたときにはがすべきか。 どうするんだろう。
= お腹空いた。
= ごちそうさま。
= Meadow、間違えて*.pycを開いたときに代わりに*.pyを開いてくれないかなぁ。
= .objects.all()[-6:]はできない。マイナスの値でスライシングできない。 まるでリストのように扱えてはいるけども、あくまでイテレータで、 遅延されたSQLクエリだから。

objects.order_by("-create_date")[:6]ってやった。 きっとSQLの中にordered by句を混ぜてくれているんだろう。


= ほうほう。joinとかはfor文に変な属性をつけることではなく、フィルタの形で実現されている。

フィルタの縦棒の前後にはスペースを入れると怒られる。

{{ tags|join:", " }}
複数のタグのANDで絞り込む機能ができた。
def tag_detail(request, tag_slugs):
    slugs = tag_slugs.split("/")
    tags = [get_object_or_404(models.Tag, slug=slug) for slug in slugs]
    items = reduce(lambda x,y :x.filter(tags=y),
                   tags,
                   models.Comment.objects)
    
    return render_to_response(
        'doukaku/tag_detail.html',
        dict(
          tags=tags,
          items=items))
こんだけ。URLが/tags/foo/bar/bazだとfooとbarとbazの絞り込み検索になります。 もちろん、存在しないタグを指定したら404。 Djangoすごー。

URLが正規表現でのマッチングだからこそできる技ですな。 自動的に/で刻んでしまうようなフレームワークでは面倒。


= んー。認証プログラムがSMTPを叩こうとして、失敗するなぁ。 自分のところでSMTPサーバが起動していることを期待しているのかな。 SMTPサーバーを自前で用意する。 XPでは元からSMTPサーバを持っているようだ。
= 自販機でお茶を買おうと思って外に出ようとしたら顔の赤いおじさんに「うちのじゃダメですか」と言われた。そして旅館内の自販機はお茶が180円だった。 外に普通に自販機があるような立地でそれはダメだろ。
= SMTPサーバを立てたらエラーメッセージが変わった。
= itermはC-Enterで最大化。 vimcolorは内部でvimを呼ぶ。
= SMTPをどう設定したらいいのかがよくわからない。 WindowsのIISでどうやってやるのか調べて解決しても、 サーバに入れたら動かなくなったりしそう。 でもアカウント作成ができないと投稿もできないしなぁ。
= http://www.red-bean.com/~bos/hgbook.pdf
= DjangoのデータベースAPIを使ったときに、 実際に発行されているSQLが何かを見る方法。

DEBUGがONなら

In [25]: from django import db

In [26]: db.connection.queries[-1]
Out[26]:
{'sql': u'SELECT "doukaku_challenge"."id","doukaku_challenge"."title","doukaku_c
hallenge"."body","doukaku_challenge"."create_date","doukaku_challenge"."comment_
count","doukaku_challenge"."code_count","doukaku_challenge"."tb_count" FROM "dou
kaku_challenge" WHERE ("doukaku_challenge"."id" = 1)',
 'time': '0.016'}
こんなんなってる。
In [30]: models.Tag.objects.all()[::-1]
Out[30]: [<Tag: <em>hoge</em>>, <Tag: Foo>, <Tag: Hoge>]

In [31]: db.connection.queries[-1]
Out[31]:
{'sql': u'SELECT "doukaku_tag"."id","doukaku_tag"."caption","doukaku_tag"."slug"
 FROM "doukaku_tag"',
 'time': '0.000'}

= NXT Python おおお。LEGO MindStormがPythonで操作できる! MindStorm買います!
= Windows用のgettextの場所を露木さんに教えてもらった。 GetText for Windows
= lxml。XPathが完全対応。全部Pyrexで書かれているので高速なんだってー。

Twisted Mind - 誰もさわらないlxmlについて。 ぼろぼろのHTMLでもパースできる。


= Meadow/Emacs memo: テキストの補完入力 ― abbrev ,定型句。 これを使うとEmacsの略語展開で展開した後のカーソルの位置とかを指定できる。
= _make-messages.py -l jaして、poを書き換えて、compile-messages.pyしても翻訳が反映されない場合、 サーバを再起動してみる。
= アカウント作成は メール送信に失敗するからここではデバッグできないや、 サーバ借りてからになるな、と思っていたけども、 話してみたら 「ローカルにメールサーバを立てて転送せずに内容だけ見たら。 メーラからリンクをクリックしなくてもURLだけコピペすればOK」 「って言うかメール送信する側のコードに手を加えられるんだからそこで表示してみたら」 ということであっさり解決。
= スタイルシートはよくわからない。 メインコンテンツとサイドメニューを80%、20%にしたときに、 サイドメニューが追いやられてしまうケースと 追いやられてしまわないケースの違いがよくわからない。
= 色々テストのせいでできたいらないユーザを消す。
In [50]: xs = django.contrib.auth.models.User.objects.filter(pk__gt=2)

In [51]: xs
Out[51]: [<User: hogehoge23>, <User: momo>, <User: momo2>, <User: momo3>, <User:
 taro>, <User: taro2>]

In [52]: [x.delete() for x in xs]
Out[52]: [None, None, None, None, None, None]
やっぱ対話的っていいね。

追記:xs.delete()でいいそうです。


= 27時過ぎに会議室を抜け出してお風呂に入った。 24時間お風呂に入れる。 しょっぱい。海水っぽい。

体固い。

お風呂に入ったら体が温まって眠気が飛んだ。おなかすいた。

おお、4:44だ!

2007年06月20日

Python温泉までにやること

とはいえ「パイソン温泉の準備をしなきゃ」なんて口走ると、知らない人の頭の中では 「温泉にパイソンとかいうものを入れたりとか…?」なんて謎な風景が広がるかも知れないので、 個人的には勝手に「Python開発合宿」と呼んでいます。「開発合宿=温泉」ってのは新人研修で習ったよね~?(ぇ

試しに「開発合宿 温泉」でググってみたところ、今度Python温泉(合宿)が行われる場所って、 「日本を担うweb開発者の間で人気の開発合宿先」なんだって! 開発合宿レポート11月号 伊東の山喜旅館にまた行ってみた | i d e a * i d e a。 旅館なのに机と椅子とホワイトボードが…。あとノートパソコンだけじゃなくて普通のモニターがあるけど、これは開発者の持ち込みなのかな?

話を戻して、Python温泉までにやりたいこと。 DjangoでCabを使って Python コード添削道場 コード募集 (Python Workshop the Edge 2007) のもっとマシなもののプロトタイプを作る。

Cab、コードやコメントに日本語が入ることを想定していないっぽいなぁ。 Cabが悪いのかPygmentsが悪いのか知らないけど、コード中の日本語は化ける。

Pygmentsの本家で日本語を試してみたら通るなぁ。

CabがPygmentsを呼び出す直前に「self.code = unicode(self.code, 'utf-8')」って入れたら化けなくなった。


= 今までhowmで日記を書いていたのだけど、 ラボに入ってからつらつら書くのがラボ内掲示板になってしまったので 全然使ってなかった。 今久しぶりに使った。

Djangoを勉強する過程で覚えた新しいことに関しては、 ラボ内掲示板に書くよりも公開した方が(そして間違いに関してはつっこまれた方が) おたがいに有益だと思うのでこちらに書くことに。

DjangoのドキュメントDjango snippets のソースと、 Djangoのblogキット のソースを読み進め中。


=

テンプレート。 {% extends "base.html" %} って書いておいて {% block main_contents %}ほげ{% endblock %} ってやればbase.htmlのなかの {% block main_contents %}~{% endblock %}で囲われた ブロックが「ほげ」で置き換えられる。 面白い。


= モデルの定義はmodels.pyにクラス定義の形で書く。
from django.db import models

class Person(models.Model):
    name = models.CharField(maxlength=30)

class Tag(models.Model):
    caption = models.CharField(maxlength=30)
   
class Code(models.Model):
    author = models.ForeignKey(Person)
    tags = models.ManyToManyField(Tag)
って書くと
BEGIN;
CREATE TABLE "app1_code" (
    "id" integer NOT NULL PRIMARY KEY,
    "author_id" integer NOT NULL REFERENCES "app1_person" ("id")
);
CREATE TABLE "app1_person" (
    "id" integer NOT NULL PRIMARY KEY,
    "name" varchar(30) NOT NULL
);
CREATE TABLE "app1_tag" (
    "id" integer NOT NULL PRIMARY KEY,
    "caption" varchar(30) NOT NULL
);
CREATE TABLE "app1_code_tags" (
    "id" integer NOT NULL PRIMARY KEY,
    "code_id" integer NOT NULL REFERENCES "app1_code" ("id"),
    "tag_id" integer NOT NULL REFERENCES "app1_tag" ("id"),
    UNIQUE ("code_id", "tag_id")
);
COMMIT;
ってSQLが生成される。 manage.py syncdbってコマンドラインで打つとこのSQLを投げて適切なテーブルを作ってくれる。 ただ、すでに作ってあるテーブルにカラムを追加したりした場合には、 どうすべきか機械が自動的に判断するのはむずかしい。 なのでmanage.py reset [app_name]でデータベースをリセットする。app_nameには作っているアプリの名前を入れる。
= 下のように書くとcreate_dateフィールドの値の逆順で並ぶ。
    class Meta:
        ordering = ['-create_date']

= 下のように書くと追加時に自動的に今の時刻にしてくれる。auto_nowなら更新時にも変えてくれる。
create_date = models.DateTimeField(auto_now_add=True)
Cabには下のように書いてあったけど、上の書き方でいいはず。
    def save(self):
        if not self.id:
            self.date = datetime.datetime.now()
        super(Rating, self).save()

= GETリクエストで副作用のある操作をしないことは、 単なるお作法ではなくて、 リンクのクリックやリダイレクタを使った CSRFの可能性をあらかじめつぶしておく「型」なんだなぁ。

においをかぐときに試験管に鼻を近づけるのではなく手で扇ぐ、 という型を守ることで、万が一いきなり突沸したりした場合のリスクを下げるようなイメージか。


= そうそう、CAPTCHAって嫌いなんだよね。 だって3回に1回くらい蹴られるから。 Oと0とかIとlと1とか9とgとかZと2とか、 そういう紛らわしいものを混ぜるのはやめていただきたい。

でも住所入力で半角英数字を蹴られるのの方がもっと嫌い。 フィールドも必要以上に刻まないで欲しい。 一つにつながっていさえすれば、ATOKが今までの入力履歴を覚えているから、 「墨田区[TAB]」で補完されるのに。


= 例えば「言語」っていうオブジェクトを作ったとして、 将来的に「言語別の解答一覧」を出したいのなら、 slugが必要。slugってのはURLにできるような文字列のことらしい。
= おおっ、verify_existsなんてのがあるのか!
models.URLField(blank=False, verify_exists=False)

= キーボードのShiftキーが壊れてしまった。 たまに認識しない。
= 自分と同じ型のオブジェクトをFKで参照したい場合は"self"
parent = models.ForeignKey('self')

= Djangoは汎用ビュー(generic view)がすごくいいという話をよく聞くので、 その汎用ビュートやらがどんな見かけなのかの一覧サイトとかないのかなぁと思ってた。 勘違いしていた。

FAQ:Django は MVC フレームワークのようですが,コントローラ (Controller) を「ビュー (view)」と呼び,ビュー (View) を「テンプレート (template)」と呼んでいます.どうして標準的な呼び方をしないのですか? より:

我々の MVC の解釈では,「ビュー」とはユーザに提示されるデータのことをいいます.つまり,データが どのように見えるか ということではなく,むしろ どの データを提示するか です.ビューは どのデータを見せるか であり, どう見せるか ではありません.この二つは明らかに違います.

というわけで,我々のケースでは,「ビュー」は特定の URL に対する Python コールバック関数になります.なぜなら,コールバック関数はどのデータを提示するかを決めているからです.

さらに,テンプレートによってコンテンツとプレゼンテーションの分離がはっきりしています.Django では,ビューはどのデータを提示するかを決めていますが,ビューは通常, どのように データを提示するかをテンプレートに委ねます.

ビューってのを見かけのことだと思ってた。


= FAQから:
私達は自分のサーバに (Subversion レポジトリ上の) 開発中のコードを直接使っており,安定に運用できています.このことを考えると,一般論として.「公式の」リリースよりはより多くの機能と少ないバグを持つ最新の開発版を使うように勧めます.

へー。

パフォーマンス上の理由から,ファイルをデータベースに置くことはありません。
ほー。
= fixtureを使うと、毎回テストデータを作らなくてもよくなる。see custom SQL から fixture に移行してみました - MiCHiLU.com
manage.py dumpdata doukaku --indent=2 > data.json.txt
こうするとdata.json.txtって名前のファイルにデータベースの中身がJSON形式で吐き出される。 一部抜粋。
  {
    "pk": "1", 
    "model": "doukaku.challenge", 
    "fields": {
      "body": "\u00e6\u008a\u0095\u00e7(以下略)
ここでこんなスクリプトを実行する。
import re
import sys
import codecs

def calc(m):
    s = m.group()
    c1 = int(s[4:6], 16) 
    assert 0xe0 <= c1 < 0xf0
    c2 = int(s[10:12], 16) 
    assert 0x80 <= c2 < 0xc0
    c3 = int(s[16:18], 16) 
    assert 0x80 <= c3 < 0xc0
    c = ((c1 % 16) * 64 + (c2 % 64)) * 64 + (c3 % 64)
    return unichr(c) # r"\u%X" % c

sys.stdout = codecs.getwriter('utf8')(sys.stdout)
data = file("data.json.txt").read()
print re.sub(r"(?:\\u00[\da-f]{2}){3}", calc, data)
そうすると下のように出力される。(一部抜粋)
  {
    "pk": "1", 
    "model": "doukaku.challenge", 
    "fields": {
      "body": "投稿を試してみたい場合はこちらへどうぞ", 
この出力結果をアプリのディレクトリの中のfixtures/initial_data.jsonってファイルに入れておけば、 次からresetしてもsyncdbの時に自動で追加してくれるようになる。
= はてなを見習って開発合宿レポート | i d e a * i d e a。 開発合宿の心構え。
合宿は1日目が勝負。そこでだいたいのめどを立てなくてはいけません。
悩んだらちょっと調べて聞く。いつもなら2時間ぐらい悩むところが数分で解決します。「あ、それは○○で調べてください」と教えていただいたこと数回。検索ワードを教えてもらうだけでもかなり違います。それで何度救われたことか・・・。
途中途中で休憩をいれるのですが、他の人の開発環境を覗くのはかなり勉強になりました。

= DjangoとIPythonのタッグはいいなぁ。 models.pyに定義したChallengeクラスをインポートして タブを押してみたらChallenge.get_previous_by_create_dateなんてメソッドがあるのが見えた。 DateTime型のフィールドを作ると自動でこういうメソッドもできるのかな。

マニピュレータってものもあるみたい。 何に使うのかはまだ知らない。


= 昨日書いたような気がしたけど書いてなかったので。 Windowsのコマンドプロンプトでmanage shellして、 インタラクティブにデータベースの中とかAPIとかいじれるモードに入ると いろいろ探検できて楽しいのだけど、デフォルトの文字コードがSJISなせいで データベースにUTF-8で入れたものとかが化けてうれしくない。

あらかじめコマンドプロンプトのプロパティでフォントが「ラスタフォント」ではなく 「MSゴシック」とかになっていることを確認した上で chcp 65001でコードページを変更するとちゃんと日本語で表示されるようになる。

ちなみにXPの話なのでVistaがどうなのかは知らない。


= Django+IPythonで遊ぶ場合、直前に評価した値を取得する目的で_を使うことはできない。 i18nに使うgettextの関数がその名前になっているせい。
= Djangoのテンプレートがいい感じに整形されるようなEmacs用のモードがないかな、と思ったらあった。 Emacs - Django Code - Trac

ダウンロードできないorz


= マニピュレータ、コメントの追加のところで使ってあった。
= IPythonで!cmdと!startをやったときに何が起きるのかは知っておくと使えるときがあるかもね。 !cmdは通常のコマンドプロンプトに入る。IPython上でもコマンド実行できるからいらないかも知れないけど、 プロンプトとかが通常表示になるし、うっかりNameError起こしたりとかもない。 戻るときにはexit。 !startは別窓でコマンドプロンプトを開く。 !start manage runserverなんてのもありかも。
= auto_now_addはなくなる予定なんだって。 フィールド定義と保存時の挙動は疎結合にすべきとの方針らしい。
= newforms ライブラリ。 「ドキュメントはここまで。詳しいことはユニットテストを参照」ってのはいいなぁ。 僕もユニットテストを書こう(そういう動機かよ!)
= forms.BooleanField(required=False)のrequired=Falseを削ると、 「チェックしないと怒られるチェックボックス」ができる。 でもデフォルトだとエラーメッセージが「このフィールドは必須です。」だなぁ。
= 下のように書いたら「'CommentForm' object has no attribute 'clean_data'」 と怒られた。
f = CommentForm(request.POST)
if f.is_valid():
    if f.clean_data['anonymous']:
「print vars(f)」してみたら、どうやらclean_dataじゃなくてcleaned_dataらしい。 newforms ライブラリ にはclean_dataって書いてあるのに。
= コメントを表示するテンプレートを再利用したくて、 module_comment.txtってのに切り出してみた。

しかし、これはコメントオブジェクトがcommentって名前でコンテキストに入っていることを仮定している。 再利用しようとした場所(あるコメントに返信を書くフォーム)では、 表示させたいコメントオブジェクトがparentって名前でコンテキストに入っているので、名前を変更したい。

Lispのletみたいに、一時的な名前を作る構文があってもおかしくないけど、 ドキュメントを流し読みしても見つからない。

Djangoのdefaulttags.pyを読んでみると、withってのがあるのがわかった。 でも使い方がわからない。 GoogleCodeSearchで"{% with"で検索してこんな例を発見

{% with business.employees.count as total %}
なるほど。できたできた。
= うーん。 乗り換え案内で明日どうやっていったらいいか調べたら、 押上→泉岳寺→横浜→伊東というコース(2時間、3390円)が安いって言われたけど 出張申請では 赤坂見附→新橋→熱海→伊東(2時間21分、2370円)ってコースで申請してあるなぁ。 あ、前者は踊り子号を使うから、乗り過ごしたら2時間後だな。 申請通りのコースで行こう。 …って出発点自体違うじゃん、何申請してるんだ自分。

有料特急の利用をOFFにすればいいんだな。

2007年06月19日

Djangoデビュー日記

Django面白い。Pythonのイントロスペクションがガンガン効く。

僕はZopeとJettyしか使ったことがないので、 RoRとかTurboGearsとかがどうなのかは知らないけど。

まず「あれこのオブジェクトなんだろう?、どんな属性を持っているんだろう?」 と思ったらtypeやdirで囲うだけですぐに見ることができるのがすごい。 サーバとか再起動しなくていいし。あ、Zopeももちろんそうだったけど。

それからshellモードがあって、それはもう普通に対話的に実行ができる。IPythonを入れるとTABで補完とかされるのですごい。


でコード投稿サイトを作る話は、露木さんに教えてもらったDjango snippetsに使われているCabってやつがBSDライセンスなのでそれを使えばいいかなと。でもこれは最新のDjangoの機能を使っているので、インストーラでインストールした僕の環境では動かない。リポジトリから最新のを取って来なきゃ。

Cabをリポジトリから取ってきた。 models.pyが8キロバイト。250行弱。viewsフォルダの中に7キロバイト、8キロバイト、3キロバイト。おおっと。これは全部読むのもさほどむずかしくない手頃なサイズってやつですかね?

しかしファイル数37、フォルダ数5、合計80キロバイト弱。そしてほとんどがテンプレートファイル。Djangoすごいなぁ。

とりあえずローカルでこれを動かすのに挑戦しよう。色々依存ライブラリがあるし。


とりあえず環境変数をきちんとする。Python2.2 ,2.3 ,2.4 ,2.5と全部入っているせいでごちゃごちゃしてたので整理。まずdjango-adminが2.5で立ち上がるように2.5のScriptフォルダを先に来るように。それからコマンドプロンプトでpythonって打ったときに立ち上がるのが2.5になるようにc:\python25を先に。でもって最後に*.pyの関連づけをPython2.5のpython.exeに。

Djangoはちゃんと動いているみたい。思ったより簡単にインストールできた。

proj1ってプロジェクトを作って中にcabをほりこんでみる。

C:\django\proj1>manage.py syncdb
Error: Couldn't install apps, because there were errors in one or more models:
proj1.cab: No module named markdown
あー。いろいろ入れないとな。

PygmentsとMarkdownをいれて、pygments.lexer.__init__.pyがエラーを出すので最後の1行をコメントアウトしたら動いた。

Sqliteでデータベースの名前を入れ忘れたせいでオンメモリに作ったみたいで、 作成はうまく行くけど読み出せなくてはまった。

adminでCabのモデルとかを見られるようになったけども、肝心のCab本体はテンプレート周りで何か問題が起きている模様。

TemplateSyntaxError: Template 'base.html' cannot be extended, because it doesn't exist
たしかにbase.htmlは配布物の中にはない。で/django/contrib/admin/templates/adminにあった。これをTEMPLATE_DIRSに追加してみた。

言語を追加するときに、Pygmentsでの名称を入れなきゃいけない。 _mapping.pyに一覧があった。

「'ascii' codec can't decode byte 0xe3 in position 195: ordinal not in range(128)」 キタコレ。

エラーが出ているところの直前に「print vars(self)」って書き加えてみる。

{'updated_date': datetime.datetime(2007, 6, 19, 23, 18, 20, 671000), 'code': 'pr
int "Hello, world!\xe3\x81\xa0\xe3\x82\x88\xef
(以下略)
ふむ…。送られてきたコードがasciiだと仮定してるんだな。でデータベースに入れるときにUnicodeにしようとして失敗するんだな。

とりあえず手前でunicodeに変換してやったら動いた。 でも今度はテンプレートのloadで「'comments' is not a valid tag library: Could not load template library from django.templatetags.comments, No module named comments」と怒られた。

django.contrib.commentsをsetting.pyのINSTALLED_APPSに追加した。

おお、やばい、あと10分で終電だ。 昨日は終電の30秒前に駅に着いた。

追記: Unicodeの問題はこのパッチを当てると直るよ→Changeset 5247 - Django Code - Trac←ちかいうちにDjangoの本体にマージされるらしい。露木さんありがとう!

2007年06月17日

日曜なのに、日記。

明日のデモの準備ができていないのが気になって 休日なのに出社してしまう罠。

もう10時半なので帰ります。 カラオケ行きたい。 ボウズマンの歌を歌いたい。(入ってねーよ)

2007年06月13日

つぶやき日記

Python コード添削道場 コード募集 (Python Workshop the Edge 2007)はおかげさまで 「お題が集まらなくてイベントができないよう(><)」という事態は避けられそうです。

個人的にはこれが2週間後のイベントのためだけのサイトで、終わったら放置プレイ、というのはちょっともったいないかもなぁと思えてきました。 プログラミング日記 - Python Workshop the Edge 2007

今のこのサイトは「コードを貼るサービス試してみたけど日本語文字化けする~」「じゃぁブログでやっちゃおうか、MovableTypeならブラウザで設定するだけでブログ増やせるし」というわけでMovableTypeになっているわけですが、コード投稿サイトに特化する上でMovableTypeはいじりにくい。Perlだし。プラグインくらいは書けるけどMovableTypeの中をハックするのはちょっと…。

というわけでPythonでブログを作るってなると何を使うのが一番いいのかなぁ。 Djangoのblogキット | スパムとか かなぁ。 aodagの日記 - TurboGearsでBlog作ってみる(その1)かなぁ。 PyBlosxom - main siteかなぁ。

やりたいことは、まずは<が表示されない問題の解決。 それから今はコメントの表示を丸ごとPREで覆っているから単なるコメントもソースコード扱いだけど、分けたいよね。 でもユーザにPREを書かせると閉じ忘れたりしたら大変だから、 TEXTAREAがコード用とコメント用とあればいいのかな。

それから、「Python2.5では動くけどPython2.4では動かないよ」とかあるから、個々のコメントにタグをつけたいね。[これはいい]タグとかあってもいいかもしれない。[エレガント]とか[実用的]とか[ワンライナー]とか(ぉ

コメントはツリー状になるといい、と思ったけど、まぁそれは面倒だったら無くてもいいか。個々のコメントにアンカーつけておけば。

ウェブアプリって何から手をつけたらいいかよくわかんないんだよねぇ…

2007年06月12日

コード添削道場日記

Python Workshop the Edge 2007 - コード添削道場 なんてのを作りました。ぜひご参加ください。

というのを書くために今日の日記。


= JavaやめてFlash使いになろうかと思った。

働く女性のためのフリーマガジンが13冊も置いてある…。

腰が痛い。

ビタミン剤は飲んで寝るといい。


= Firebugは便利! でもFirebugはCSSエディタじゃない。保存はできない。

6月30日のESPer2007はPython Workshop the Edge 2007とかぶってる

アリナミンEXプラスとネオビタミンはEXは、 成分の量が5桁目まで一緒だ。 値段は倍違う。

Cybozu Developer Network: Python調査報告 (2006/10)。 「setuptools は distutils の上位互換であるため、setuptools を使用しない理由はありません。」 Python拡張を作るなら。

「大人の超刺激的キャンディLaMenta」は確かに刺激的だった。

LaMenteを食べてウィルキンソンを飲む。

ニコブ吹いた http://www.nicob.jp/?m=default&a=view&p=nb2919 そんなところに村上水軍はねーよw

こんな感じのパターンにマッチする単語を探してみた PAT = re.compile(""" ^[bcdfghjklmnpqstvxz]* [aeiouryw]+ [bcdfghjklmnpqstvxz]* 全体の1割、10万単語中1万件。

今日覚えた英語 WLOG: Without loss of generality

>>> [[]]*3
[[], [], []]
>>> _[1].append(1)
>>> _
[[1], [1], [1]] 
>>> list([] for i in range(5))
[[], [], [], [], []]
>>> _[0].append(0)
>>> _
[[0], [], [], [], []] 

ためしにGRINEditで可視化しているところをビデオ撮影してみたけど、やっぱり雰囲気くらいしかわからないなぁ。

>>> "%02X" % 255
'FF'
>>> int("FF", 16)
255 

ディクショナリー・コンプリヘンションはないのか、とか思ってたけど簡単にできるじゃん

>>> dict((k, k*10) for k in range(5))
{0: 0, 1: 10, 2: 20, 3: 30, 4: 40}

= って感じかな? あ、でもラボのログから引っ張ったから家で作ったTシャツや棚の話がないな。

2007年06月10日

豪雨日記(日曜)

昨日土曜日の帰り道で急に土砂降りになって驚いた。 すぐやむかと思ったけどやまないので諦めて土砂降りの中を帰ってきた。 折りたたみ傘を持っていたのに膝上まで濡れた。

朝は雨がやんでいたので、計画通り秋葉原まで自転車で行って 喫茶ルノワールで執筆活動でもしようかと思ったけど、 二度寝をして昼になったら雷が鳴って土砂降りになった。 行かなくてよかった。

今週のラボ内ひとりごとをエクスポートしてくるのをすっかり忘れていたので、 今週分のダイジェスト日記はありません。 まぁ、バイトコードをいじったり、他のことをしたりとか、そんな感じ。 もっぱらバイトコードをいじると言うよりは、 バイトコードをどういうデータ構造にして、どういうアルゴリズムでまたバイトコードに戻すか、 ってあたりをやっていたような気もする。

土曜日にカードキーを部屋の中に置いたまま外に出て、閉め出された。 警備員さんに頼んで開けてもらった。

キャノンのワードタンクきたよ! これ会話辞典には台湾語も載ってるんだねぇ。 ひさしぶりは中国語では好久不見了だけど台湾語では眞久無看見[口阿]になる、と。 まぁ台湾でも中国語が通じるのだけど。 面白い。 あと一部だけど発音が付いているので、四声がわかる。 会話調の単文を自分で発音してから辞書のを聞くといかにイントネーションがめちゃくちゃかがわかる。

部屋を片付けて掃除機をかけようかな。

2007年06月09日

バイトコードいじり超楽しい~日記

しまった。明日はSonyCSLのオープンハウスに行くから、 今日のうちに色々やっておくべきことがあったかも。 掲示板の先週からの差分だけをhowmに出力とか。

今週はちょっと燃えすぎて燃え尽き気味。 バイトコードいじりが面白かった。 と、過去形で語りたくなる感じ。

残りのピースをはめ込めばジグソーパズルが完成するんだけどねぇ…。

Pythonのバイトコードを、ジャンプの関係とかを解析してグラフ状にするところはできたので、 あとはそのグラフを加工してもう一度バイトコードに戻せるようにすれば楽しいはず。 せめてバイトコードに戻すところまで。 可能ならグラフのパターンマッチを簡単にするライブラリも。


= やることがいっぱいあって時間が足りていない。 バイトコードはやる予定じゃなかったのに面白くてついついやってしまった。 うーん。 今週末は休日出社かなぁ。
= おなかいっぱい。 ご飯を食べずに。 キャベツとほうれん草とレタスと、総菜屋で買ってきた唐揚げとなんか中華っぽい野菜炒め。 総菜屋は便利だが、味付けが濃い傾向がある。 総菜屋のサラダで満腹になるまで食べるとドレッシングのカロリーだけでも結構あるはず。 総菜屋のサラダは調味料で、主食は自分で調理したドレッシングのかかっていないサラダ。

一方朝ご飯はお団子。


= Sony CSLのオープンハウスに行ってきました。 前回100人くらいだった懇親会に350人来たそうで、大変なことになってました。 僕はタマネギとサラダと後からやってきた鮨3個と鶏肉のようなもの1個を食べました。

茂木さん目当てとの噂。

僕は軽くアルコールが入った後、抜けていく過程でたまにトランスするのですが、 バッテリーの残りが少なかったので駅前のマンガ喫茶で充電。600円。

マンガ喫茶と帰りの電車でバイトコード操作ライブラリのブラッシュアップ。 ファイル名が「test*.py」から「sample*.py」に変わってきているのが ステージの変かを感じさせる(笑)。

詳しいことはそのうちラボブログに書くけど

def printArgs(func):
    codeGraph = analyze(foo)
    N = foo.func_code.co_argcount
    (head, tail) = weave(
        [build("LOAD_FAST", i) for i in range(N)],
        build("BUILD_TUPLE", N),
        build("PRINT_ITEM"),
        build("PRINT_NEWLINE"),
        codeGraph[0])

    # overwrite co_code
    code = CodeHack.getValue(func)
    code[4] = toBytes(serialize(head))
    CodeHack.setValue(func, code)

    return func

if __name__ == "__main__":
    def foo(a, b, c):
        pass

    printArgs(foo)
    foo(1, 2, 3)
これで(1, 2, 3)と表示されます。 バイトコードをいじると、ジャンプの飛び先が変わって大変なんだけども、 それを自動でやってくれるライブラリになっています。 いやはや。頭に引数の表示機能を入れるくらいなら生でバイトコードをいじってもできたんだけどね。 いろいろな応用を考えたときに「頑張らなくても簡単にできる方法」 を作っておいた方がいいと判断したので。

今週ほとんどこれしかしてないなぁ。 本当は再来週の月曜日にデモがあるのでGRINEditをもっとブラッシュアップしたかったのだけど。 それをほっぽりだしてしまうくらい面白かったのです、これは。

バイトコードを見ていると色々発見があります。 x & yは二項演算ですが、x and yもPython的には二項演算ですが、 でも、バイトコード的にはif文です。 if 0: ...はPython的にはif文ですが、バイトコード的にはコメントです。 ごっそり消えています。

あと、if文は条件式を評価した値を、then節やelse節の中にまで持って行ってます。 なのに冒頭でPOP_TOPして捨ててしまっています。 Pythonでは代入が式じゃないせいで下の前半のようには書けないわけですが、

# NG
if m = re.match(pattern, string):
   print m.group()

# OK
m = re.match(pattern, string):
if m:
   print m.group()
下のようなのは文法的にはOKです。
if STORE(m = re.match(pattern, string)):
   print m.group()
でもって、Python的には「条件式に代入文は入らない」というルールがありますが、 バイトコード的にはありません。 というわけでこのSTOREって関数の呼び出しをローカル変数への代入に置き換えてしまえば…

import re

def func(x):
    if STORE(m = re.search("'.*?'", x)):
        print m.group()

storable(func)
func("aaa'bbb'ccc") #-> 'bbb'

できました。 storableの中身の詳しい話とかは[あとで書く]。

もう眠い。 明後日の日曜日はビルが休館日でたぶん入れないので、 明日ラボに行ってやらなきゃいけないことをやる。

2007年06月05日

Re: ようやく継続がちゃんと分かった気がする

Yのほぼコード置場 - ようやく継続がちゃんと分かった気がする

前略、仕事中なのでコードだけ。

>>> def test():
	r = yield
	while True:
		r = yield ("received: %d" % r)

		
>>> g = test()
>>> g.next()
>>> g.send(1)
'received: 1'
>>> g.send(10)
'received: 10'

追記: ジェネレータって面白いですよね。

2007年06月04日

地震上野レオナルド日記

自分がふらついているのが地面がゆれているのか 疑問に思うくらいの規模の地震はたくさん起こっている気がするが、 今日の地震は大きくて長かった。 で、テレビとかないのでどうやって情報を得ようか考えた結果、 携帯電話でFMラジオが聴けることに気がついたので初めて聞いてみた。NHK。 地震なんてなかったかのようにスルーされてた。

んー。

この程度の地震はスルーの対象なのか? 長くゆれたので遠いと考えると、 滋賀とか岩手とかあたりですごいことになってたりしそうで怖いんだがな。 緊急ニュースにならないってことはそうでもないのか。

震源地は茨城県南部 ( 北緯36.1°、東経140.1°)で震源の 深さは約60km、地震の規模(マグニチュード)は4.6と推定されます。
なーんだ。

ちなみに地震が起きて真っ先にしたことは、 下着を穿くことでした。お風呂上がりだったので。


= 今週末は予定なし。 さて、引きこもってキーボードを叩いていても仕方がないのでどこかへ出かけるとするか。
= 昨日は吾妻橋まで行ったあたりでそのまま浅草通りを西進すれば上野駅にたどり着くことに気づき決行。 上野駅に当たったところでどこから上野公園に行けばいいかがわからずさまよっているうちに 御徒町駅に…。商店街的なものがたくさん。

御徒町まで行けば秋葉原までもう一駅だったのか。今気がついた。

上野駅周辺でダビンチ展をやっていることを知ったのでさっそく行ってみた。 第一会場が15分待ちだったので華麗にスルーして第2会場へ。

davinci_annunciazione_u00.jpg

この絵、よくよく見てみるとなんか横長で、 天使はつんのめりすぎているし、 マリアは左右で手の長さが違う。 でも、これは絵の右斜め前から見るために書かれたものなんだそうだ。

という説明を見てから第一会場に行ったら待ち時間なしで、実物を見られた。 そして当然みんな正面から見ている。ちょっとした優越感。


= 東急ハンズ、渋谷、新宿、池袋、と西の方ばっかりで遠いなぁと思っていたけど、 北千住にもあるみたい。 マルイの一フロアだからそんなに広くはなさそうだけど、まぁ行ってみるか。

なんと、東武伊勢崎線から1本で6分くらいみたいだ。 じゃぁ自転車ですぐかな、と思ったけど隅田川の屈曲部が間にあって道がややこしい。 距離的にも上野よりさらに1キロくらい遠いみたいだ。 とりあえず電車で行こうっと。


= いろいろ買ってきたけど眠くなって早寝。
= 5時起床。

Bloks Duoを買ってきたけど、http://www.blokus.com/index.htmでオンライン対戦できた。

昨日はDVDとかテレビとか見られるようにしたけども、 まだテレビを見るまでの手順が複雑で面倒。

数ヶ月ぶりにNHKの朝のニュースを見た。 うーん。代わり映えしてないなぁ。 ドイツでサミットがあるのかー。

うどんやの生姜飴を買った。ウィルキンソンの好きな人にはおすすめ。

ぬぬぬ、買い物で付いたポイントを銀行に入れられるって。 そんなダイレクトな。

DVD見るマシンはMacなのでBUFFALOのチューナに付いてきたテレビを見るためのソフトが動かない。 どうしたらいいのか調べる。あと録画も見れない。これはたぶんMP4のコーデックがないんだろう。

はっ、もう7時半。

2007年06月02日

今週ラボで何をしてたか日記

6月1日

ラボ掲示板からhowmにエクスポートするスクリプトを書いた。

GoogleDesktopを入れているとCtrl2回で検索ウィンドウが出ることに気がついた

あっ、そうか 今まで

for(Object o: r.marginalEdge){
        IEdge e = (IEdge)o;
}
ってやってたけど
for(IEdge e: (Vector<IEdge>)r.marginalEdge){
        
}
でいいのか。

少しずつかしこくなる。

口内炎がしみる…

わかった、右手首が痛くなったのはマウスを使ったからだ。 普段動かさない筋肉を使ったから筋肉痛になったんだ。

手首回すと引っかかる。かなりやばい。 腱鞘炎なりかけ。 それもこれもマウスのせいだ。

FujiSankei Business i. TOPIC/目で動くマウス 障害者、広がる仕事の場(2004/9/11) http://www.business-i.jp/news/top-page/topic/art-20040910214939-SQQMQUGOYK.nwc

フット用マウス http://www.edikun.co.jp/footmouse/footmouse.htm 高いなー

足用マウス「足技Ⅱ」:就労支援機器のページ http://www.kiki.jeed.or.jp/pro/g312210.html んー。

NOHANDS MOUSE http://www.footmouse.com/

HHKの真ん中にトラックポインタが付けばいいんだが。

悲しい。 ハッピーハッキングキーボードは ハックできないように特殊なもので止めてあった。 ハックしてゲームパッドのジョイスティックでも解体してつっこもうと思ったのに。 ハックできないハッキングキーボード…。

Python、re.finditerしらなかった。便利。

m = re.match(...
if m:
    doSomething(...
じゃなくて
for m in re.finditer(...
    doSomething(...
という選択肢。

from xml.sax.saxutils import unescape

残念、&quot;とかは置換してくれない。


= 5月31日。

シングルトンパターンは事実上のグローバル変数だけども、 Javaの場合どこからでもアクセスできるのはクラスの名前だけで、 一般的な操作では同じクラスの名前が束縛されているオブジェクトが変わることはない。 つまり単一代入なのだ。 だからわりと有効に機能しているんだなぁ…と気がついた。

だからクラスが単一代入でないPythonで形だけまねてシングルトンパターンにしたって無意味だし、 仮に単一代入の名前空間を実装したところで、それ自体は単一代入でないなにかに入れないとアクセスできないわけなので根本的な解決にはなっていない。

for (int i = 0; i < vertexList.size(); i++) {
とか
for(Iterator i = allEdges.values().iterator();i.hasNext();){
とか
for(Object o: allEdges){
とかが混在している。成長の爪痕。

Javaで複数の値を返したくて、ローカルにネストしたクラスを作ったが 当然、それを返してもローカルのクラスはアクセシブルじゃないので無意味

フィールドの位置に移動したら見慣れぬエラーが。 >アクセス可能な型 Selection のエンクロージング・インスタンスがありません。 型 Selection のエンクロージング・インスタンスで割り振りを限定する必要があります (たとえば x.new A() で、x は Selection のインスタンス)。

今回はResultにstaticをつければ済む話だけど 仮にあえてやるとすれば Result result = new Selection(null).new Result(); でコンパイルが通った。 こんな書き方がありだとは… っていうか要するにnewの構文がおかしいんだよなぁ。 Selection.createInstance().Result.createInstance() ってことだもんなぁ。

(6月1日注:Pythonでタプルでくくって多値を返すようなことがやりたければ new Object[]{a, b, c}して返すのが近いだろうか。)

しまった、振り込まないといけないものがあったんだ お、みずほ銀行赤坂見附支店は24時間営業か。

配列をCollectionに変えるのに、Arrayにそういうメソッドがないなぁと調べてみたらArraysというクラスにasListってのがあった。 (6月1日注:以下しばらくの内容はラボブログに書きました)

(6月1日注: あー、こうやって公開してもよさそうな情報は一般公開しているけど、 mixiの書き込みで見つけた有名なハッカーのコメントなんかは 一応mixiの書き込みである以上公開しちゃいけないよな)

orz 銀行行くの忘れてた 今から行くと終電微妙 明日振り込むと発送が月曜日だろうなぁ そして受け取るのが次の日曜とかになりそうだ


= ジェネレータにsendをつけようという提案 http://www.python.org/dev/peps/pep-0342/ > to make them usable as simple coroutines ジェネレータをコルーチンとして使えるように、だそうな。

yieldを使いこなすと何かおもしろいことができそうだなぁ。sendとか。

send関係ないけどreturnの代わりにyieldって書くだけで遅延評価

>>> def foo(x):
        yield force(x) * 100

>>> def force(x):
        if "next" in dir(x):
                return force(x.next())
        return x

>>> a = foo(foo(5))
>>> a

>>> a.next()
50000
でも、Haskellが偉いのは遅延評価ができるからではなく、 パターンマッチが必要最小限だけその遅延されたのをforceしてくれるから。 こんな「全部展開しちゃう遅延評価」なんてHaskellの足元にも及ばない。

Pythonのバイトコードを実行する方法ってないのかなぁ。 sys.exechogehoge("\xff\xff\xff...")って感じの。

ぎゃー gzip -rってやったら 再帰的にすべてのファイルが*それぞれ別個に*圧縮された!orz

http://amaznode.fladdict.net/ あうー。

ASとXML http://members.netmadeira.com/killer/xmlrpc/ http://livedocs.adobe.com/flex/2/langref/XML.html

The Gutenberg Webster's Unabridged Dictionary by Project Gutenberg and Webster - Project Gutenberg http://www.gutenberg.org/etext/660

e 96161
i 76258
a 74936
r 64251
t 62125
o 61884
n 58064
s 52677
l 51265
c 40423
u 32216
p 28245
m 27593
d 26654
h 23204
g 19984
y 18587
b 16372
f 11089
v 8557
w 6244
k 5838
x 2781
z 2722
q 1709
j 1333
7文字以上の文字列で、J単体より出現頻度の高いものは存在しない。 6文字の ation$ 2317 が最大。

やはり冒頭より末尾に特徴が出るみたい。4グラムの結果:

ion$ 3629
tion 3517
ess$ 2981
ous$ 2955
ate$ 2574
atio 2451
ing$ 2217
ness 1960
ble$ 1900
ent$ 1779
able 1597
ical 1473
ity$ 1435
ter$ 1397
3グラムだと冒頭のも少し混ざってくる。

PythonにもHaskellみたいにconcatMapがあればいいのに。

sum(map(f, xs), [])
destinationから誤字を生成した結果:
13.9351636617 7 ation => g $desting$
7.67656794942 7 ation => e $destine$
5.10651757985 7 ation => ess $destiness$
4.67049248373 7 ation => t $destint$
4.41360062079 5 in => r $destration$
therapist:
68.5779159295 6 pi => li $theralist$
50.4951876028 5 ap => al $theralist$
48.9651783919 5 ap => m $thermist$
39.5948029493 5 ap => s $thersist$
30.8206729864 5 ap => t $thertist$
うーん。serapistとかが候補に挙がってこないなぁ。
= 5月28日。Jython関係であがく。
= 5月25日。
paths must precede expression(略
原因: *.* を引用符で囲まないとbashが展開しちゃう

レゴブロックでピタゴラ装置のレールを造る場合、 普通に作ってもでこぼこでうまく進まないし、 試しに上が平らな部品で覆ってみたけども 「そこが平らなレール」では「レールから離れるときの位置」に自由度があるためにコントロールしにくい。「離れる向き」にもばらつきが出るし。

というわけで今思いついたのだけど http://dgla.jp/shop/detail.php?seq=4461&&kw= これを使ってV字の谷を作ればいいんじゃないだろうか。 ブロック2ます分のスペースで十分ビー玉が通るし。

Vector params = (Vector) Convert.make(
        "[{'methodName': 'dump', 'params': []}]");

らくちん。

でも「Convert」って感じではないなぁ


= という感じの一週間でした。

あと僕が詳しいことを書いちゃいけないと思うので書かないけど、 今日から新しい人が入ってきました。 公式の発表をこうご期待。

あ、なんだもう公開してあった。 IT戦記 - そろそろサイボウズ・ラボについて一言いっとくか。 これ面接とかは僕が入る前に終わっていたので、僕が知ったのはスケジュール帳の「6月1日 天野さん入社」っての。で、どの天野さんかなぁ、とスルーしていたらなんとこの天野さんか! それを知ってから今日までどんだけしゃべりたかったことかw 社会人というものは業務上知り得たことをみだりにしゃべらない自制心が必要なのだなぁとしみじみ思ったものだよ。で、秘密は守り抜けました!えらい?(普通)