KZKY memo

自分用メモ.

Python DI Framework Pinject

Googleの人が作った (Google公式のProjectではない)Python DI Frameworkらしい.
結構簡単に使える.

数人で開発する場合に,全体のロジックやSWアーキは自分で握って,
クラス単位で他人が作成したものに差し替えたいという場合は,
スクリプトといえど,DIが使いたくなる.
(モジュール/クラスを思っても見ない感じにいじられるとたまったものではない)

Installation

sudo pip install pinject

Condensed Summary

  • .pyに書く,yml/json/xmlでない
  • configurationにはbinding specを使う
  • arg nameとclass nameが対応していると暗黙にDI
  • @inject() __init__()はこのクラスにはBindするという意味
  • argname to class/instance両方可能
  • singleton/prototypeのscopeは当然可能
    • provider bindings: bindings_spec#provideで使用するメソッド
    • provider methods: bindings specの中のメソッド
  • Binding spec 同士の依存も可能
  • annotationで指定も可能

dependency injection

  • implicit
import pinject


class OuterClass(object):
    def __init__(self, inner_class):
        self.inner_class = inner_class
    pass


class InnerClass(object):
    def __init__(self):
        self.forty_two = 42
    pass

obj_graph = pinject.new_object_graph()
outer_class = obj_graph.provide(OuterClass)
print outer_class.inner_class.forty_two

auto init

import pinject


class ClassWithTediousInitializer(object):
    @pinject.copy_args_to_internal_fields
    def __init__(self, foo, bar, baz, quux):
        pass

cwti = ClassWithTediousInitializer('a-bar', 'a-foo', 'a-baz', 'a-quux')
print cwti._foo

Binding specs

import pinject


class SomeClass(object):
    def __init__(self, long_name, long_name2, outer_class):
        self.long_name = long_name
        self.long_name2 = long_name2
        self.outer_class = outer_class
        pass
    pass


class SomeReallyLongClassName(object):
    def __init__(self):
        self.foo = 'foo'
        pass
    pass


class SomeReallyLongClassName2(object):
    def __init__(self):
        self.hoge = 'hoge'
        pass
    pass


class InnerClass(object):
    """
    """
    
    def __init__(self, ):
        """
        """
        self.name = "inner_class"
        pass
    pass


class OuterClass(object):
    """
    """
    
    def __init__(self, inner_class):
        """
        """
        self.inner_class = inner_class
        pass
    pass


class MyBindingSpec(pinject.BindingSpec):
    def configure(self, bind):
        bind('long_name', to_class=SomeReallyLongClassName)
        bind('long_name2', to_class=SomeReallyLongClassName2, )
        bind('outer_class', to_class=OuterClass)
        #bind('inner_class', to_class=InnerClass)
        pass
    pass
    
obj_graph = pinject.new_object_graph(binding_specs=[MyBindingSpec()])
some_class = obj_graph.provide(SomeClass)
print some_class.long_name.foo
print some_class.long_name2.hoge
print some_class.outer_class.inner_class.name
import pinject


class ClassOne(object):
    def __init__(self, foo):
        self.foo = foo
        pass
    pass

    
class BindingSpecOne(pinject.BindingSpec):
    def configure(self, bind):
        bind('foo', to_instance='foo-')
        pass
    pass


class ClassTwo(object):
    def __init__(self, class_one, bar):
        self.foobar = class_one.foo + bar
        pass
    pass


class BindingSpecTwo(pinject.BindingSpec):
    def configure(self, bind):
        bind('bar', to_instance='-bar')
    pass

    def dependencies(self):
        return [BindingSpecOne()]
    pass

obj_graph = pinject.new_object_graph(binding_specs=[BindingSpecTwo()])
class_two = obj_graph.provide(ClassTwo)
print class_two.foobar

scope

  • prototype (defaultはsingleton)
import pinject


class SomeClass(object):
    def __init__(self, foo):
        self.foo = foo
        pass
    pass


class SomeBindingSpec(pinject.BindingSpec):
    @pinject.provides(in_scope=pinject.PROTOTYPE)
    def provide_foo(self):
        return object()
    pass


obj_graph = pinject.new_object_graph(binding_specs=[SomeBindingSpec()])
some_class_1 = obj_graph.provide(SomeClass)
some_class_2 = obj_graph.provide(SomeClass)
print some_class_1.foo is some_class_2.foo

provider bindings

  • scopeがprotyotypeになるような別のやり方
  • 次のprovide_fooをfooにしてもTrueになるが,provide_fooではinstanceを常に作る
import pinject


class Foo(object):
    def __init__(self):
        self.forty_two = 42
        pass
    pass


class SomeBindingSpec(pinject.BindingSpec):
    def configure(self, bind):
        bind('foo', to_class=Foo, in_scope=pinject.PROTOTYPE)
        pass
    pass


class NeedsProvider(object):
    def __init__(self, provide_foo):
        self.provide_foo = provide_foo
        pass
    pass


obj_graph = pinject.new_object_graph(binding_specs=[SomeBindingSpec()])
needs_provider = obj_graph.provide(NeedsProvider)
print needs_provider.provide_foo() is needs_provider.provide_foo()