Javaの場合、Dependency Injectionは純粋なOOPとして機能します。つまり、実装するインターフェイスを提供し、フレームワークコードで定義済みのインターフェイスを実装するクラスのインスタンスを受け入れます。
Pythonについても、同じ方法で行うことができますが、Pythonの場合、その方法はオーバーヘッドが大きすぎると思います。それでは、Pythonの方法でどのように実装しますか?
これがフレームワークコードであるとしましょう:
class FrameworkClass():
def __init__(self, ...):
...
def do_the_job(self, ...):
# some stuff
# depending on some external function
最も素朴な(そしておそらく最高の)方法は、外部関数をFrameworkClass
コンストラクターに提供し、do_the_job
メソッドから呼び出すことを要求することです。
フレームワークコード:
class FrameworkClass():
def __init__(self, func):
self.func = func
def do_the_job(self, ...):
# some stuff
self.func(...)
クライアントコード:
def my_func():
# my implementation
framework_instance = FrameworkClass(my_func)
framework_instance.do_the_job(...)
質問は短いです。これを行うためのより一般的に使用されているPythonの方法はありますか?または、そのような機能をサポートするライブラリがありますか?
トークンを使用して認証を処理するマイクロWebフレームワークを開発すると想像してください。このフレームワークには、トークンから取得したID
を提供し、そのID
に対応するユーザーを取得する関数が必要です。
明らかに、フレームワークはユーザーや他のアプリケーション固有のロジックについて何も知らないため、クライアントコードは認証を機能させるためにフレームワークにユーザーゲッター機能を注入する必要があります。
DIの代わりにスーパーおよび多重継承を使用する方法についての議論については、 レイモンド・ヘッティンガー-スーパーをスーパーと見なしました!-PyCon 2015 を参照してください。ビデオ全体を見る時間がない場合は、15分にジャンプしてください(ただし、すべてを見ることをお勧めします)。
このビデオで説明されている内容を例に適用する方法の例を次に示します。
フレームワークコード:
class TokenInterface():
def getUserFromToken(self, token):
raise NotImplementedError
class FrameworkClass(TokenInterface):
def do_the_job(self, ...):
# some stuff
self.user = super().getUserFromToken(...)
クライアントコード:
class SQLUserFromToken(TokenInterface):
def getUserFromToken(self, token):
# load the user from the database
return user
class ClientFrameworkClass(FrameworkClass, SQLUserFromToken):
pass
framework_instance = ClientFrameworkClass()
framework_instance.do_the_job(...)
これは、Python MROがgetUserFromTokenクライアントメソッドが呼び出されることを保証するためです(super()が使用される場合)。コードがPython2.x。
ここで追加された利点の1つは、クライアントが実装を提供しない場合、例外が発生することです。
もちろん、これは実際には依存性注入ではなく、多重継承とミックスインですが、問題を解決するためのPython的な方法です。
プロジェクトで依存性注入を行う方法は、 inject libを使用することです。 ドキュメント を確認してください。 DIに使用することを強くお勧めします。 1つの関数だけでは意味がありませんが、複数のデータソースなどを管理する必要がある場合は、多くの意味を持ち始めます。
あなたの例に続いて、それは次のようなものになるでしょう:
# framework.py
class FrameworkClass():
def __init__(self, func):
self.func = func
def do_the_job(self):
# some stuff
self.func()
カスタム関数:
# my_stuff.py
def my_func():
print('aww yiss')
アプリケーションのどこかで、定義されたすべての依存関係を追跡するbootstrapファイルを作成します。
# bootstrap.py
import inject
from .my_stuff import my_func
def configure_injection(binder):
binder.bind(FrameworkClass, FrameworkClass(my_func))
inject.configure(configure_injection)
そして、この方法でコードを使用できます。
# some_module.py (has to be loaded with bootstrap.py already loaded somewhere in your app)
import inject
from .framework import FrameworkClass
framework_instance = inject.instance(FrameworkClass)
framework_instance.do_the_job()
pythonには空想がないので、これは得ることができる限りPythonicであると思います(モジュールには、パラメーターなどで注入するデコレーターのようなpythonの甘さがあります)インターフェイスやタイプヒンティングなど。
質問に答えるを直接行うのは非常に難しいでしょう。私は本当の質問だと思います:pythonはDIのネイティブサポートを持っていますか?そして、答えは、悲しいことに、いいえです。
少し前に、Pythonにしたいという野心を持った依存性注入マイクロフレームワークを作成しました- Dependency Injector 。これは、コードを使用する場合のコードの例です。
"""Example of dependency injection in Python."""
import logging
import sqlite3
import boto.s3.connection
import example.main
import example.services
import dependency_injector.containers as containers
import dependency_injector.providers as providers
class Platform(containers.DeclarativeContainer):
"""IoC container of platform service providers."""
logger = providers.Singleton(logging.Logger, name='example')
database = providers.Singleton(sqlite3.connect, ':memory:')
s3 = providers.Singleton(boto.s3.connection.S3Connection,
aws_access_key_id='KEY',
aws_secret_access_key='SECRET')
class Services(containers.DeclarativeContainer):
"""IoC container of business service providers."""
users = providers.Factory(example.services.UsersService,
logger=Platform.logger,
db=Platform.database)
auth = providers.Factory(example.services.AuthService,
logger=Platform.logger,
db=Platform.database,
token_ttl=3600)
photos = providers.Factory(example.services.PhotosService,
logger=Platform.logger,
db=Platform.database,
s3=Platform.s3)
class Application(containers.DeclarativeContainer):
"""IoC container of application component providers."""
main = providers.Callable(example.main.main,
users_service=Services.users,
auth_service=Services.auth,
photos_service=Services.photos)
この例のより詳細な説明へのリンクは次のとおりです- http://python-dependency-injector.ets-labs.org/examples/services_miniapp.html
それが少し役立つことを願っています。詳細については、次をご覧ください。
典型的なPython開発者の好みではなく、その言語機能のために、DIとおそらくAOPは一般にPythonicとは見なされません。
実際のところ、メタクラスとクラスデコレータを使用して、 100行未満の基本的なDIフレームワーク を実装できます。
侵襲性の低いソリューションの場合、これらの構造を使用して、カスタム実装を汎用フレームワークにプラグインできます。
また、Pinject、オープンソースpython Googleの依存性インジェクターもあります。
ここに例があります
>>> class OuterClass(object):
... def __init__(self, inner_class):
... self.inner_class = inner_class
...
>>> class InnerClass(object):
... def __init__(self):
... self.forty_two = 42
...
>>> obj_graph = pinject.new_object_graph()
>>> outer_class = obj_graph.provide(OuterClass)
>>> print outer_class.inner_class.forty_two
42
そして ソースコードはこちら
Python OOP実装IoCと依存性注入は、Python world。 Python用。
だから私の solution は:
# Framework internal
def MetaIoC(name, bases, namespace):
cls = type("IoC{}".format(name), Tuple(), namespace)
return type(name, bases + (cls,), {})
# Entities level
class Entity:
def _lower_level_meth(self):
raise NotImplementedError
@property
def entity_prop(self):
return super(Entity, self)._lower_level_meth()
# Adapters level
class ImplementedEntity(Entity, metaclass=MetaIoC):
__private = 'private attribute value'
def __init__(self, pub_attr):
self.pub_attr = pub_attr
def _lower_level_meth(self):
print('{}\n{}'.format(self.pub_attr, self.__private))
# Infrastructure level
if __== '__main__':
ENTITY = ImplementedEntity('public attribute value')
ENTITY.entity_prop