Previous month:
October 2009
Next month:
December 2009

Google App Engine入門:実行効率を犠牲にせずに開発効率だけを上げるテクニック

 一つ前の富豪プログラミングのエントリーともつながる話だが、Google App Engineは「ちゃんとスケーラビリティを考慮してアプリケーションを作るには何に気をつけなければならないか」を勉強するには絶好の環境だ。そこで今回は、その「ケチな大富豪的なプログラミング」の実践編。

 Google App Engine上のアプリをいくつか書いているうちに、必要に迫られて自然発生的にできてきたのが、gdispatchという数十行のコードからなる小さなモジュール(ソースコードはgithubに置いてある)。これをGoogle App Engineに標準で付いて来るwebappと組み合わせてフレームワークとして使っている。

 gdispatchを設計する上で重視したのは、

(1)Google App Engine上でのアプリの開発を効率化する上で「明らかにこれがあると開発効率が格段に向上する」というもの以外は含めない
(2)実行効率を最優先に考え、初期化時にできることと毎回のアクセス時のみにしかできないことを明確に分け、毎回のアクセス時にすることを極力少なくして実行時のオーバーヘッドを最小にする

の二つである。

 その結果、gdispatchには以下の二つの関数と二つのデコレータのみが入っている。

memoize (デコレータ)

 これはPythonを使う人たちの間では良く使われるデコレータのパターンで、関数の戻り値をクロージャを使ってキャッシュすることにより、実行効率を上げる仕組みである。Google App Engineの場合、サーバーが一度メモリーにロードしたモジュール(およびそれに関連するグローバル変数やクロージャ)は、アプリへのアクセスが途絶えてアプリそのものがサーバーから追い出されない限りはメモリーに常駐していてくれるので、memoizeとはとても相性が良い。

kwargs(デコレータ)

 これは、HTTPリクエストをプロセスするRequestHandlerのメソッドを実装する際に、getやpostに渡されたパラメータを、関数へのnamed argumentとして渡された形でのコーディングを可能にするデコレータである。

 このデコレータを使うと、

class NewAccountHandler(webapp.RequestHandler):

    def post(self):

        email = self.request.get('email')

        message = self.request.get('password')

        account = self.request.get('account')

    ...

 

と書く代わりに、

class NewAccountHandler(webapp.RequestHandler):

    @gdispatch.kwargs
    def post(self, email, password, account):

...

と書けるのである。

 このデコレータのソースコードは以下の通りである。inspectモジュールを使って元の関数(original_func)の引数を調べて、それを元にself.request.get()で取得すべきパラメータの名前のタプル(args)を作っている部分が(getメソッドを置き換える)decorated_func の外側であることに注目して欲しい。こうしておけば、inspect.getargspec()を呼ぶのはモジュールがメモリに読み込まれた時一度だけで、それ以降のHTTPリクエストの処理の際には、クロージャの形でキャッシュされたargsを参照するだけである。

def kwargs(original_func):

    """ This decorator allows RequestHandlers to receive get/post parameters as named arguments """

    import inspect

    argspec = inspect.getargspec(original_func) 

    args = tuple(argspec[0][1:])

    def decorated_func(rh):

        kwargs = dict([(arg, rh.request.get(arg)) for arg in args])

        return original_func(rh, **kwargs)

    return decorated_func

route、run (ファンクション)

 URLマッピングのルールの記述をしやすくするroute()とrun()はセットで使う。webappフレームワークでプログラムを書く場合、正規表現で記述したURLとRequestHandlerの関連づけは

def main():

    application = webapp.WSGIApplication([('/', MainHandler),

                                          ('/new', NewAccountHandler),

                                          ('/post', NewMessageHandler),

                                         ],

                                         debug=True)

    wsgiref.handlers.CGIHandler().run(application)

のようにWSGIApplicationに渡すリストで指定するのが一般的な方法だが、マッピングのルールは各RequestHandlerクラスと一緒に記述した方が便利である。そこで作ったのが、route()という関数でこれを使えば、

gdispatch.route(lambda: ('/new', NewAccountHandler))

class NewAccountHandler(webapp.RequestHandler):

    def post(self):

        ...

のように記述することが可能になる。そして、main() では

def main():

    gdispatch.run()

とするだけで良い。

 このケースでも、route() 関数を呼ぶのはモジュールをメモリにロードした時だけで、その後のHTTPリクエストの処理時には、初回に作ったリストを使ってマッピングを実現することにより実行時のオーバーヘッドを最小にしている。

 gdispatchが提供している機能のいずれにも共通するのは、Google App Engineの「一度メモリに読み込んだモジュールは(グローバルメモリやクロージャも含めて)アプリがサーバーから追い出されないかぎりメモリに存在し続ける」という性質を最大限に利用して、実行効率を犠牲にせずに開発効率だけを上げるているという点である。

 こんな風に痒いところに手がとどくようなプログラミングが実際のデプロイメント環境を使って簡単にテスト・実践できてしまうところがGoogle App Engineのすばらしいところだと思う今日この頃である。


「富豪プログラミング」もいいけど「けちな大富豪」になるべき

 Ruby on Railsに代表されるDRY(Don't Repeat Yourself)スタイルのフレームワークは、手っ取り早くサイトを立ち上げるのにはとても便利だ。特にRailsのActive Recordの様に、ランタイムにダイナミックにコードや設定ファイル(もしくはそれに相当するもの)を生成してくれる仕組みは、情報を一カ所のみに記述することによりミスを減らすという意味でもアジャイルな開発という意味でも重要である。

 ただ、この手のフレームワークを使う場合に一つ気をつけなければならないのは、それがスケーラビリティの面で商用に耐えられるものか、という点である。特に、その手のダイナミックなコードや設定ファイルの生成(Railsの場合だとデータベース上のテーブルのスキーマに基づいたActive Recordクラスのダイナミックな生成)が、最初にサイトにアクセスが来た時に一度だけ実行されるものなのか、アクセスがあるたびに毎回ゼロから作り直しているものなのかで、リソースの使用量に大きな違いが出てくるので気をつけなければならない。

 こんなことを言うと、「今は『富豪プログラミング』の時代だから、そんなことは気にしなくて良い」という人がいるが、そんな発想は大間違いである。

 「富豪プログラミング」というコンセプトは「今やハードやネットワークコストがとても安くなったのだから、その手のものを贅沢に使って開発コストを安くすることを優先すべき」という発想。設定ファイルが不要なO/Rマッピングなどは良い例だが、だからと言って一度だけ実行すれば十分なマッピングをサイトにアクセスがあるたびに毎回する必要は全くない。

 別の言い方をすれば、「富豪プログラマー」として、手間がかかるだけで人為的ミスを生じやすいO/Rマッピングのような単純作業をコンピュータに任せるのは多いに結構だが、そのマッピングがどのタイミングで何回行われてそのコストがどのくらいかを全く気にしないというのは単なる怠慢だ、という話である。

 Rails関連の話を聞いていると、「プログラミングのしやすさ」にばかりに話題が集中していて、この手の実行効率の議論・検討・理解が十分になされていないように感じる(こことかを読むとRailsの抱えているジレンマが良く見える)。CPUの使用時間に応じて課金されるクラウドコンピューティングの時代になりつつある今、このあたりはウォーレン・バフェットを見習って、徹底的に「けちな大富豪」であるべきだと思うんだがいかがだろう。


なぜ気象庁のホームページには地震速報のフィードがないのか

 先日、地震速報のフィードがないものかと気象庁のホームページを探していてこんな記述を見つけた。 

(財)気象業務支援センター 
 天気予報や天気図、注意報、地震情報などの即時情報は(財)気象業務支援センターから必要経費によりオンラインやファックスなどで入手することができます。 データの種類や料金などの詳細については(財)気象業務支援センターのホームページに掲載されていますのでご覧下さい。

 普通に考えれば、オンラインでのデータの配布コストは限りなくゼロに近いのだから、税金を使って集めた気象情報や地震速報などはRSSやJSONのフィードとして気象庁のホームページから無料で配布して当然である。

 ところが実際には、FAXや電話でしか情報が流通できなかった時代の名残りか、気象業務支援センターという財団法人が一括して、それも有料で配布を担当しているのだ。

 ならば、さっさとそんな仕組みは廃止して気象庁から直接フィードを配信するようにすれば良いと思えるが、そうは簡単に行かないのが現状である。

 というのは、気象業務支援センターが気象庁の人たちの天下り先だからだ。自分が世話になった先輩が役員として勤めている、ひょっとしたら自分が将来世話になる場所かも知れない。そう考えると、一方的に切ることは心情的に非常に難しい。私自身がその立場にあっても、簡単にはできないと思う。

 典型的な「天下りの弊害」である。

 この問題が難しいのは、これがモラルの問題ではない点にある。人と人とのつながりを大切にする日本人の場合、そんな立場に置かれたら一方的に切ったりしないのが普通である。「先方に相談」したり「様子を見て」いるうちに自分の任期が終わることを待つのが普通である。任期が来て「自分の時代にことを荒立てなくてすんだことに胸をなでおろす」のが普通である。

 そんなごく普通の良識的な人たちが集まっていながら、なぜか組織として必ずしも国民の利益になる行動を取りにくくしてしまうところが、「天下りというシステム」の怖いところである。

 無理矢理に脱官僚を押し進めたり予算カットをする必要はないと思うが、官庁とその監督下の財団法人との間に利害関係を生み出してしまう天下りだけは全面禁止が良いと思う私である。


高速道路無料化をするなら「エコカーのみ無料」にすべき

 題名だけで私が何を伝えたいかはほぼ理解していただけると思うが、私としては、高速道路の全面無料化には、

  • 今後の日本経済にとってプラスになるとは思えない
  • 環境に優しくない
  • さらなる渋滞をまねく
  • その財源がどこにあるのか不透明

という理由で反対。どうせやるのであれば「エコカーとして国から認定された車のみ無料」とすべきだと思う。これにより、

  1. 日本の消費者の間でエコカーの人気が上がる
  2. →日本の自動車メーカーのエコカーへの開発投資意欲が上がる
  3. →日本にエコカー関連の知的財産が蓄積する
  4. →日本の自動車メーカーの海外での競争力が増す
  5. →日本の景気が良くなる
という相乗効果を狙う。財源はガソリン税および無料対象外の車の高速道路使用料金を当てる。

 さらに、エコカーの基準を毎年厳しくすることにより、「日本で自動車メーカーを経営しようとしたら、毎年厳しくなる環境基準をクリアする車を作りつづけない限りやっていけない」状態を人為的に作り出し、(携帯電話とは逆に)「日本という島国で強烈に進化して海外でやたらと競争力の強いガラパゴス・カー」を作りだすのだ。


Twitterを使ったブクマを格段に簡単にするTiny Quote

 興味深い記事やブログエントリーをネットに見つけた時に、それにひと言コメントを付けて投稿、というのはTwitterで良く見かける行動パターンの一つ。それも他の誰かのためにというよりは自分自身への備忘録という意図の場合もしばしばあるので、ある意味で「ソシアル・ブックマーク」的な使いかたに近づいてきているとも言える。

 ただ、そんな時に140文字の中に記事中で気になった文章を引用しつつ自分の意見を述べてかつURLを詰め込む、というのはなかなか難しい。

 そこで、先日の Tiny Message のバックエンドをこの問題の解決に使えないかと作ったのが、Tiny Quoteというブックマークレット。それなりに動き始めたのでここで公開する。

TinyQuote (←このリンクはクリックせずにブックマークバーにDrag&DropもしくはCopy&Pasteして登録してください)

 このブックマークレットをブラウザーのブックマーク・バーに登録しておけば、ワンクリックで(1)自分のコメント+(2)引用文章+(3)記事のURLを140文字制限に縛られずに投稿できる(現在のところSafariとFirefoxの最新版のみで動作確認済み)。

 ページの一部を引用したい場合は、あらかじめその部分をマウスで選択してからブックマークレットをクリックする。ただし、あまり長い文章を引用しようとするとURLの長さ制限にひっかかってしまうので(ブラウザーにURLが長すぎると文句を言われる)、英文で10行程度、5行程度が限界のようだ。

 ちなみに、ここで使っている Tiny Message のAPIは後日正式に公開する予定である。このブックマークレットのソースを読めば使い方は分かると思うので、それまで待てない人はどうぞ。


Google App Engine入門:フレームワークの選択

 Google App Engine向けのアプリを作る際に最初に悩んだのはフレームワークの選択。Google App Engineにはwebappという最低限の機能を持ったフレームワークが付いて来るが、Python使いの人たちの間では、DJangoというフレームワークが広く使われているらしいし。かといって、あまり大きなフレームワークを使うと、パフォーマンスのチューニングとかもしにくくなるし、フレームワークそのもののバグや制限に悩ませられる可能性もある。

 そんな中で増井君が見つけてくれてまず試したのが、Junoというフレームワーク。DJangoと比べると遥かに小さく、WebappよりもURLのルーティングのメカニズムとかが充実している。

 そこで一旦はアプリをJunoの上で作り始めたのだが、Junoのソースコードを見ているうちにいろいろと気に入らないところが出て来た。不必要にオプションが多いし、同じことをする方法を何通りも用意している点が気に入らない。そしてどうしても我慢ができなかったのが、レスポンスをグローバル変数を通して行っていること。ちょっとこれでは使えない。

 そこで着目したのが、JunoにあってWebappに不足しているのはURLルーティングのローカルな記述だけだという点。Webappの場合、URLのルーティングの規則を以下のように一カ所に固めて書かねばならないのだが、

class LoginHandler(webapp.RequestHandler):

    def post(self):

    ... loginの実装
 

class LogoutHandler(webapp.RequestHandler):

    def get(self):

    ... logoutの実装

application = webapp.WSGIApplication([('/login', LoginHandler),

                                        ('/logout', LogoutHandler),

                                        ... URLからHandlerへのマッピングリスト

                                        ])

wsgiref.handlers.CGIHandler().run(application)

Junoの場合は以下のように、処理をするファンクションにデコレータの形でURLを記述できるのである。

@juno.post('/login')

def process_login_form(web):

    ... loginの実装
 

@juno.route('/logout')

def logout(web):

    ... logoutの実装


juno.run()

 そこで思いついたのが、同じような仕組みをwebappでできるようにするモジュールを作るということ。それさえできれば余計なフレームワークなしでwebapp上でアプリを作ることができる。

 そして試行錯誤の作ったのが、gdispatchというモジュール。使い方はほぼJunoと同じで、

@gdispatch.route(lambda:('/login', LoginHandler))

class LoginHandler(webapp.RequestHandler):

    def post(self):

    ... loginの実装

@gdispatch.route(lambda:('/logout', LogoutHandler))

class LogoutHandler(webapp.RequestHandler):

    def get(self):

    ... logoutの実装

gdispatch.run()

とするだけの話。これがあれば、RequestHandlerの数が増えて複数のファイルまたがったとしても、関連するURLマッピングのルールはそれぞれのRequestHandlerと一緒に記述できるのでとても便利である。

 ということで、結論としては「フレームワークとしては、Google App Engineに標準で付いて来るwebappに必要最小限の手を加えて使うのが一番良い」というのが私なりの結論である。

 ちなみに、gdispatchはgithubに公開しておいたので試したい方はどうぞ(https://github.com/snakajima/gdispatch)。


「リニアにスケールするように作れる」からこそのGoogle App Engine

 Google App Engineを使った最初の作品 Tiny Message (https://tinymsg.appspot.com)をリリースしてまだ20時間経っていないが、設計の過程でいろいろと学べたことがある。

 その中でも一番収穫として大きいのは、「Google App Engineを使えば、リニアにスケールするサービスを作ることが可能」だということが実感できたこと。

 もちろん、Google App Engine上に作ったからと言ってすべてのアプリがリニアにスケールするわけではなし、どんなアプリでもそう作れるわけではない。Entity Groupの構成を間違えればそこがボトルネックになるし、Queryの二重ループなんかを書いたら、すぐにタイムアウトしてしまう。

 リニアなスケーラビリティを持つDatastoreの上で作るとは言え、やはりDatastoreの仕組みをちゃんと理解してデータモデルから設計する必要もあるし、プログラムも丁寧に書く必要がある。場合によっては、アプリの仕様から見直さなければならないケースもある。しかし、そういったいくつかの約束事さえきちんと守って作れば、いくらユーザーが増えようとデータベース上のEntity数が増えようとアクセス数に応じてCPUの使用時間と通信量が直線的に増えるだけの「リニアなスケーラビリティ」を持ったアプリケーションを作ることができる、ということは脅威的である。

 mixiにしろ、Twitterにしろ、リレーショナル・データベース上に作られたウェブサービスは、ユーザーが増えるにつれてデータベースの構成を変更しながらスケーラビリティを確保しなければならない、という宿命を背負っている。ユーザーの数が100万人から1000万人に増えた時には、単にマシンの台数を10倍にすれば良いという話ではなく、それに応じてデータベースを新たにクラスタリングさせたり、スケーラビリティの確保のために新たなデータキャッシュの仕組みを導入したり、ということがどうしても必要になる。その結果、マシンの数が10倍ではなく20倍必要になったり、スケーラビリティのことだけを専任で担当するエンジニアが何人も必要になったりする。

 今回 Tiny Message を作ることによってはっきり分かったのは、App EngineのDatastoreの特性を活かしたアプリを作れば、その手のことを一切心配せずに、100万ユーザーにでも1000万ユーザーにでも自動的にスケールすることができるようにサービスを作ることが可能だ、ということ。それも、私のように片手間でサービスを立ち上げて、ほとんど手間もお金もかけずに運営して行くことが可能だということ。

 Tiny Messageは、まだリリースしたばかりのとても小さなアプリだが、万が一ユーザー数が大爆発したときに備えて、ユニークなURLを生成する際に必要なトランザクションによるスレッド間のコンフリクトを最小限にするように24万個のEntity Groupを(オンデマンドで)作ってそれぞれにカウンターを持たせて個別にトランザクションさせる、という設計にしてある。Entity Groupが24万個あれば、相当な同時接続数になったとしても二つのHTTPリクエストが同時に同じEntity Groupを使ってユニークなURLを生成しようとする可能性は限りなくゼロに近い。

 そこも含めて (というかそこが一番難しかったのだが)、Tiny Message はすべてのコードパスがユーザー数やエントリー数には関係なく固定時間で終わるように設計してあるので、トラフィックが10倍に増えれば単にCPUの使用量が10倍に増えるだけ、というリニアなスケーラビリティが確保できているのだ。

 そんな設計のため、わずか20時間走らせただけで、今後トラフィックが増えて来た時にどうスケールするかが、おおよそ予測できる。現在、一番時間がかかっているのがデータベースへの書込みが必要なメッセージの投稿で、平均して400メガサイクル強のCPUを使っている。そこから予想するに、1日あたり1〜2万メッセージの投稿までは「無料Quota」の範囲でやっていける、と予想できるのだ。

 もちろん、その予想は「GoogleのDatastoreがリニアにスケールする」という前提のもとのものなので100%確実な話ではないが、少なくとも「確実にどこかで限界が来る」ことが見えているリレーショナル・データベースの上で作るのとは大きな違いがある。


Twitterに140文字以上つぶやきたい時のためのサービス、Tiny Message

 Google App Engineでプログラムを書き始めて1週間ほど経つが、ようやくDatastoreの基本が分かってきたので、サービス運営の経験値を積むためにもアプリを一つリリースしてみることにした。

 サービス名は、Tiny Message。Tiny URLはTwitterに書くURLを短くしてくれるサービスだが、こちらはTwitterに書くメッセージそのものを短くするサービス。

 使い方はいたって簡単。Tiny Messageのホームページ(https://tinymsg.appspot.com/)でメッセージを書き、"make my tweet"というボタンを押すとTwitter投稿用の要約(最初の100文字)+URLが表示されるので、それをコピペしてTwitterでつぶやけば良いという仕組みである(メッセージの全文を読みたい人にはURLをクリックしてもらう)。

 こんなサービスがチャチャッとすぐにリリースできてしまうところがApp Engineのすばらしさ(このアプリに使った時間は全部で5時間ぐらい)。私みたいに「プログラムを書くことだけに集中したい」人にはたまらない魅力だ。

 まだできたてホヤホヤなので、私自身のテストで把握しきれていないバグがあるかも知れないので、テストの意味も含めてぜひともたくさんの方々にお試しいただきたいと思う。もし不具合が見つかった場合には教えていただけるととても助かる。


12月3日にAndroidに関して講演します

 久しぶりに日本で講演をすることになったので、ここで告知させていただく。詳しくは日経エレクトロニクスのアナウンスメントを見ていただくのが良いと思うが、今回は「Android家電の衝撃」というテーマのいくつかの講演+パネルディスカション。

 技術的に突っ込んだ話というよりは、ビジネス上の意味だとか、Googleの戦略という話になるとは思うが、UIEvolutionの創設者としてモバイル・組み込み業界でいろいろとビジネスをして来た経験や、iPhoneディベロッパーとしてiPhoneアプリを開発・販売して来た経験を通したいろいろな話ができると思う。

 それにしても、最近は色々と試したいもの・勉強したいものが多くて困る。ここ一年ばかりどっぷりと浸かって来たのがiPhone SDK、それに最近はHTML5、CSS3、Google App Engine、Pythonが加わり忙しくてしかたがない。

 まあ、この業界にいるかぎりこうやって日進月歩の技術を勉強しては、誰もいない領域に踏み出して、失敗を繰り返しながらちょっとずつそれを先に進める、ということを繰り返して行くしかないし、それが楽しくてこの業界にいるのだから文句を言う筋合いのものでもないのだが、新しい言語や開発環境をマスターするにはそれなりの時間がかかるし、そこで何か意味のあるもの・価値を生み出すものを作ろうとしたらもっと時間がかかる。

 そんな中で、無視できない存在になって来たAndroidというプラットフォーム。携帯電話向けのOSというだけでなく、さまざまなネット端末の共通プラットフォームとしてAndroidの可能性という話から、Google が Chromeの延長として位置づける Chrome OS まで、ものの価値がどんどんとクライド側に移動していくこの時代にデバイスビジネスというものがどうあるべきか、という話はとても悩ましい話である。

 先日も日本の家電メーカー向けの経営コンサルタントをしている知り合いと話していたのだが、彼に言わせると日本の大企業の一番の弱点は、経営陣(=お抱え運転手付きの待遇を受けるレベルになった執行役員たち)がこの手の新しい技術だとかの勉強を若い人たちに任せてきりにしてしまって、そういうものを理解してこそ初めてできる経営判断のようなものがまったく出来ていない点にあるという。

 欧米の企業に主要な知的所有権をがっちりと握られ、アジアのメーカーに価格競争で追い上げられ、さらにクラウドに価値が移るにつれデバイスのコモディティ化が加速する、という今の日本の家電メーカー・デバイスメーカーの危機的状況を考えれば、まずは「お抱え運転手の全面廃止」「執行役員たちの意識革命」から始めなければならないのにも関わらず、あと3〜4年後に定年を迎える執行役員たちが問題を先送りにして、「少なくとも自分が執行役員を勤める間だけはなんとか会社が存続してくれればあとの問題は後継者に任せられる」という当事者意識の欠如、が一番の問題だと彼は言う。

 という意味では、今回の話なんかは、そんな執行役員たちに一番聞かせたい話ではあるんだが、お抱え運転手付きの執行役員が日経エレのセミナーに来るとはどうも想像しにくいんだが、どうなんだろう。


Python入門:デコレータとは

 前から常々思っていることだが、何かについて勉強する一番効率的な方法はそれを誰かに教えること。人に教えようとすると、それなりに準備をしなければならないし、自分の頭の中を整理しなければならない。また教える過程でするどい質問をされたり間違いを指摘されて、さらに勉強を強いられることもある。

 私がこの手の「入門編エントリー」を書くのは、ほとんどの場合「自分自身の理解をより深めたい」ことが一番の目的であるが、ブログの場合、教室などと違って「その道の達人」みたいな人たちがツッコミを入れてくれるケースもしばしばあるので、そのメリットは何倍にもなる。

 先日のクロージャに関するエントリーなどは良い例で、「そんな用途にはmemoizeというデコレータが便利」などの指摘がいただけだけであれを書いた価値があるというもの。

 そこで、今日はPythonのデコレータに関して。デコレータがPythonという言語に導入された経緯に関してはここに詳しい経緯が書いてあるが、ひと言で言えば、それまでのPythonでクラスメソッドを定義するには、

def foo(self):

    メソッドの本体

    ...

foo = classmethod(foo) #ここで実はクラスメソッドだと宣言

としなければならなかったのだが、これでいかにも後付けで言語として格好が悪い(手紙の冒頭に「あなたは美しい、あなたが好きだ、あなた無しでは生きて行けない」とさんざん書いておいて、最後に「これはあなた向けに書いたものではなく、単なる『詩』です」と追記するようなもの)、ということで導入されたのが以下のデコレータである(最初に『詩』だと宣言しておけば、ラブレターと誤解されることもない)。

@classmethod

def foo(cls):

    メソッドの本体

    ...

 興味深いことに、Pythonの言語を定義している人たちは、この@classmethodというデコレータを特別扱いせず、一般化してだれにでもデコレータを作れるように言語を拡張した。そのおかげで、それ以来色々なデコレータが作られ、さまざまなところで活用されている。先に出て来たmemoizeもその一つである。

 簡単な例として、「俳句を返す関数」を「短歌を返す関数」に変更してしまう haiku_to_tanka デコレータを実装してみよう。例えば、

def furuike():

    return u"古池や蛙飛びこむ水の音"

 という俳句を返す関数があった場合、それが短歌を返すようにデコレーションするには、

def haiku_to_tanka(original_func):

    def decorated_func(*args):

        return original_func(*args) + u" それにつけても金の欲しさよ"

    return decorated_func

というデコレータを前もって定義しておき、

@haiku_to_tanka

def furuike():

    return u"古池や蛙飛びこむ水の音"

としてあげるだけでよい。先のclassmethodデコレータを同じく、これは実質的には

def furuike():

    return u"古池や蛙飛びこむ水の音"
furuike = haiku_to_tanka(furuike)

と等価である。 


 なのでその後に関数furuike()を呼ぶと、オリジナルのfuruike()ではなく、デコレータによって再定義された関数(decorated_func)が呼ばれ、それがオリジナルのfuruike()が返した俳句に下の句を追加した上で短歌として返すことになるのだ。


 haiku_to_tanka関数内部で定義されているdecirated_funcはhaiku_to_tankaに渡されたoriginal_funcを参照している点に注目して欲しい。これにより、haiku_to_tankaに渡された(つまりデコレートされた)関数(オリジナルのfuruike)が再定義される関数の中に閉じ込められる形(つまりクロージャ)になっているのだ。


 ちなみに、先日教わったばかりのmemoizeというデコレータ。すでに何通りかの実装がされているのだが(例1例2)、どれもここで見せるサンプルとしてはいまいち分かりにくいので、クロージャを使って出来る限りシンプルに実装してみたのがこれ。


def memoize(original_func):

    cache = {}

    def decorated_func(*args):

        try:

            return cache[args]

        except KeyError:

            cache[args] = original_func(*args)

            return cache[args]

    return decorated_func

 このデコレータは、先の kaiku_to_tanka デコレータと同じく、オリジナルの関数 original_func をクロージャの形で閉じ込めた新たな関数を作り出す。同じクロージャ内には、関数へのパラーメータをキーとしたcacheというディクショナリを用意しておき、キャッシュ済みであればそれを返すし、そうでなければ(except KeyError: でキャッチしている)オリジナルの関数を呼び出した上でその値をキャッシュしてから返す。