Make組ブログ

Python、Webアプリや製品・サービス開発についてhirokikyが書きます。

Pyramidでzope.interfaceを使う

Pyramidでzope.interfaceを使う

Pyramidzope.interface を使うときの話

zope.intreface

zope.intreface_ が面白いと最近知った。

実装を持たない「インターフェース」だけ定義して、 そこに後で実装をもたせる、というかんじ(超ざっくり)

折れ線グラフとしてのインターフェースをILinechart、 なにやら描画してくれるインターフェースをIRendererとして考えてみる。:

import zope.interface


class IRenderer(zope.interface.Interface):
    def render():
        """なんか描画する的"""


class ILinechart(zope.interface.Interface):
    series = zope.interface.Attribute("""Y軸の値みたいな""")
    category = zope.interface.Attribute("""X軸の値みたいな""")


@zope.interface.implementer(IRenderer)
class LinechartRenderer(object):

    __used_for__ = ILinechart

    def __init__(self, context):
        self.context = context

    def render(self):
        return str(zip(self.context.series, self.context.category))

こんなかんじか。 嬉しいのは:

  • インターフェースだけ先に書いといて実装はあとから持たせられる。
  • 継承みたいに書いた時点で関係が固定されるわけじゃない。

とかかな。正直面白半分で使い始めてる節あるので何とも。

zope.interface のドキュメント読んでこのへん読んどけば捗りそう:

Pyramidにおけるzope.interfaceの使い方

zope.interface とか zope.component でもアダプターの登録ができますが、 それは Pyramid でやりましょう。

Registry というものに登録する。 まぁconfigとかrequestにひっついてるから:

# config 経由で登録して
config.registry.registerAdapter(LinechartRenderer,
                                (ILinechart,), IRenderer, '')

# request 経由で取ると
adapted = request.registry.getAdapter(linechart, IRenderer, '')

# 良い感じ
adapted.render() # '[(0, 0), (1, 1), (2, 4)]' とか

でまぁ:

config.registry.registerAdapter(LinechartRendererJSON,
                                (ILinechart,), IRenderer, 'json')
config.registry.registerAdapter(LinechartRendererHTML,
                                (ILinechart,), IRenderer, 'html')

とかname変えて登録していくとさらに良い (この場合rendererのメソッドにもたせたほうがいいかもだけど)。

お作法

そんな感じで、アプリケーション書くときに

  • __init__.pyでconfig.registry.registerAdapter
  • view_callable内でrequest.registry.getAdapter

とかやっちゃうわけだけど、これはまぁ行儀良くないらしい。 zope.interface を意識しないといけないのはライブラリやフレームワークの開発者であって ユーザー(ライブラリ等を使って開発する人)には見せたくないとのこと[要出典]。

まあたしかに、もっとわかりやすい書き方してよって気になるしね。

そのお行儀の良い書き方というのは簡単で:

  • config.add_directiveでdirectiveを追加
  • そのdirective経由でアダプター登録
  • requestを受け取るAPIとしての関数から、アダプトされたオブジェクトを返す

というもの。 さらに venusian を使って、directive経由でアダプター登録してたのを デコレーターで書いてやることができる。

まぁこんなかんじになるのか(renderer_configとto_rendererは自分で書くのよ):

@renderer_config('',
                 chart_type='linechart')
def str_linechart(linechart):
    return str(zip(linechart.series, linechart.category))

renderer = to_renderer(linechart, '')
renderer.render()

このrenderer_configのなかではconfig.set_rendererとか呼び出して登録してやるといい。 さながらPyramidのview_configとconfig.add_viewみたいなもんである。

まあ一見に如かずなのでrebecca.todictを読めばいいと思う:

これを参考に私もpyramid_tochartというのを書いてるので、こっちも参考になるかも:

ただまあ良い書き方を模索してるところ