phpでよくあるコードで、こんなものがあります。
<?php
class Foo {
private $_bar = null;
public function getBar() {
if ($this->_bar === null) {
$this->_bar = Bar::getFromFooId($this->getId());
}
return $this->_bar;
}
}
$foo = new Foo();
$foo->getBar();
$foo->getBar(); // キャッシュが利用される.
特に Twig などのテンプレートエンジンではテンプレート内で {{ foo.bar }}
と書いたら $foo->getBar();
が実行されるので、キャッシュをしておかないとなんども同じクエリをDBに発行する可能性があります。
Python で同じことをプロパティを利用して実現するとしたら、次のようになります。
class Foo(object):
_bar = None
@property
def bar(self):
if self._bar is None:
self._bar = Bar.from_foo_id(self.id)
return self._bar
foo = Foo()
foo.bar
foo.bar # キャッシュが利用される
これをもっと簡潔にするのが、 werkzeug
というライブラリにある werkzeug.utils.cached_decorator
というデコレータです。 (実際には、 Bar.from_foo_id()
が None
を返した時も正しくキャッシュできるので、より優れています)
from werkzeug.utils import cached_property
class Foo(object):
@cached_property
def bar(self):
return Bar.from_foo_id(self.id)
php で7行のコードが、 Python では 3行になりました。キャッシュを実現するための退屈なコード (boilerplate code) がなくなって、本質的なコードだけを書くことができるので、読みやすく、バグを埋め込みにくく見つけやすい、メンテナンス性が高いコードになっています。
この cached_property
はデコレータとディスクリプタという機能を使って実装されているのですが、 Python 組み込みの property
, staticmethod
, classmethod
も同じ機能を使って実装されています。
プロパティを持っている言語は幾つかありますし、 php も将来実装するかもしれませんが、 Python は一段階メタな言語機能を実装して、プロパティのような機能は単にその言語機能を利用する組み込みオブジェクトとして実装しているので、構文はシンプルなのに、より柔軟で、現実世界のゴチャゴチャしたアプリケーションをよりすっきり書くことを可能にする能力を持っています。
この記事は主に php や Java のようにオブジェクトシステムが静的な言語のユーザーに Python に興味を持ってもらうことを目的として書いています。 (php は動的型付けだがそれ以外の部分はむしろJavaに近い静的言語)
JavaScript や Ruby のような動的言語なら同じようなことができると思うので、ぜひその方法を紹介してください。できるだけ「こんな書き方もできるぜ(でも実際には使わないけどな)」じゃなくて、現実世界の日々のプログラムを楽にしてくれるような polished な方法をお願いします。