Google 製の DI フレームワークの Pinject を試してみた

Google が作っている DI フレームワークPinject というのがあるのを知った。
Injector とどう違うのか試してみた。
詳しくは GitHub の Pinject のドキュメント参照。

# -*- coding: utf-8 -*-
import pinject


class Base(object):
    def save(self, path):
        raise NotImplemented()


class FileStorage(Base):
    def save(self, path):
        return 'file save'


class RiakCSStorage(Base):
    def save(self, path):
        return 'riak save'


class Storage(object):
    def __init__(self, Storage):
        self.storage = Storage()

    def save(self, path):
        return self.storage.save(path)     
       
class BindingSpec(pinject.BindingSpec):
    def configure(self, bind):
        bind('Storage', to_class=FileStorage)
        
       
if __name__ == '__main__':
    inject = pinject.new_object_graph(binding_specs=[BindingSpec()])
    service = inject.provide(Storage)
    print(service.save('foo'))

基本形。アノテーションとか無くて、暗黙の了解で注入する。
BindingSpec クラス内の bind() で注入先の変数名を指定してる。
実行すると

file save

と表示される。
FileStorage じゃなくて to_class=RiakCSStorage にすると、

riak save

と表示される。


暗黙は嫌だ*1、BindingSpec クラス、注入先の Storage クラスを読んだら明示的に何が注入されるのか見たい場合は、デコレータを使って明示する。

# -*- coding: utf-8 -*-
import pinject


class Base(object):
    def save(self, path):
        raise NotImplemented()


class FileStorage(Base):
    def save(self, path):
        return 'file save'


class RiakCSStorage(Base):
    def save(self, path):
        return 'riak save'


class Storage(object):
    @pinject.annotate_arg('Storage', 'Storage-Anno')
    def __init__(self, Storage):
        self.storage = Storage()

    def save(self, path):
        return self.storage.save(path)
        

class BindingSpec(pinject.BindingSpec):
    @pinject.provides('Storage', annotated_with='Storage-Anno')
    def provide_storage(self):
        return RiakCSStorage # RiakCSStorage() とすればインスタンスが渡る

    
if __name__ == '__main__':
    inject = pinject.new_object_graph(binding_specs=[BindingSpec()])
    service = inject.provide(Storage)
    print(service.save('foo'))

BindingSpec クラスでプロバイダーを指定してやる。


Injector にせよ Pinject にせよ元々は GoogleGuice が元ネタだからとても似ている。
作成したコードは Injector を使ったのよりシンプルに見える。
良いなと思ったのが Bind 先の変数名に文字列が使える事。
Injector は Key() で指定したオブジェクト名だったり、bind 時に設定したオブジェクトだったりするのでシンプル。


ただし Flask と連携する前提だと Flask-Injector が既にある分そっちの方が楽。
Pinject の方だと他にも Partial injection とかできるようだけど未調査。


因に Injector の作者による Pinject との違いについて。
Pinject: A dependency injection library for python. : Python

*1:Explicit is better than implicit