web-dev-qa-db-ja.com

なぜPythonではIoC / DIが一般的ではないのですか?

Java IoC / DI は、Webアプリケーション、ほぼすべての利用可能なフレームワーク、およびJava EE。一方、大きなPython Webアプリケーションもたくさんありますが、Zopeのほかに(これはコーディングが本当に恐ろしいはずだと聞いています)IoCは_であまり一般的ではないようですPython world。 (私が間違っていると思う場合は、いくつかの例を挙げてください)。

もちろん、Pythonで使用可能な一般的なJava IoCフレームワークのクローンがいくつかあります。たとえば、 springpython です。しかし、それらのどれも実際には使用されていないようです。少なくとも、そのようなものを使用する Django または sqlalchemy + <insert your favorite wsgi toolkit here>ベースのWebアプリケーションに迷ったことはありません。

私の意見では、IoCには合理的な利点があり、たとえばDjango-default-user-modelを簡単に置き換えることができますが、PythonでのインターフェイスクラスとIoCの広範な使用は少し奇妙で、"Pythonic"ではありません。しかし、IoCがPythonで広く使用されていない理由を誰かが説明しているかもしれません。

281
tux21b

実際、DI/IoCはthatPythonでは珍しいとは思いません。 isは、DI/IoCframeworks/containersです。

それについて考えてみてください:DIコンテナは何をしますか?それができます

  1. 独立したコンポーネントを完全なアプリケーションに接続する...
  2. ...実行時に。

「一緒に配線する」と「実行時に」の名前があります。

  1. スクリプティング
  2. 動的

したがって、DIコンテナーは、動的スクリプト言語のインタープリターに他なりません。実際、言い直してみましょう。典型的なJava/.NET DIコンテナーは、お粗末な、時にはXMLベースの構文を備えた、本当に悪い動的スクリプト言語のくだらないインタープリターに他なりません。

Pythonでプログラミングするとき、美しくてすばらしいスクリプト言語を自由に使えるのに、なぜugくて悪いスクリプト言語を使いたいのでしょうか?実際、それはより一般的な質問です。ほとんどすべての言語でプログラミングする場合、JythonとIronPythonを自由に使えるのに、なぜい悪いスクリプト言語を使用したいのでしょうか。

要約すると、DI/IoCのpracticeはPythonでもJavaの場合と同じくらい重要です。同じ理由。ただし、DI/IoCのimplementationは言語に組み込まれており、多くの場合非常に軽量なので完全に消えます。

(ここでは、類推のための簡単な余談です:アセンブリでは、サブルーチン呼び出しはかなり重要です-ローカル変数とレジスタをメモリに保存し、戻りアドレスをどこかに保存し、呼び出しているサブルーチンへの命令ポインタを変更し、サブルーチンが終了したときに何らかの方法でサブルーチンに戻るように手配し、呼び出し先が引数を見つけることができる場所に引数を配置するなど。IOW:アセンブリでは、「サブルーチン呼び出し」は設計パターンであり、サブルーチン呼び出しが組み込まれたFortranは、人々が独自の「サブルーチンフレームワーク」を構築していました。サブルーチンフレームワークを使用しないという理由だけで、サブルーチン呼び出しはPythonでは「珍しい」と言っていただけますか?

ところで:DIを論理的な結論に導くと思われる例については、 Gilad BrachaNewspeak Programming Language とその主題に関する彼の著作を見てください:

187
Jörg W Mittag

その一部は、Pythonでのモジュールシステムの動作方法です。モジュールからインポートするだけで、一種の「シングルトン」を無料で入手できます。モジュール内のオブジェクトの実際のインスタンスを定義すると、すべてのクライアントコードがそれをインポートして、実際に機能する完全に構築されたオブジェクトを取得できます。

これは、オブジェクトの実際のインスタンスをインポートしないJavaとは対照的です。これは、常に自分でインスタンス化する必要があることを意味します(または何らかのIoC/DIスタイルのアプローチを使用します)。静的なファクトリメソッド(または実際のファクトリクラス)を使用することで、すべてを自分でインスタンス化する手間を軽減できますが、そのたびに実際に新しいメソッドを作成するというリソースオーバーヘッドが発生します。

46
TM.

Djangoは、制御の反転をうまく利用しています。たとえば、データベースサーバーは構成ファイルによって選択され、フレームワークは適切なデータベースラッパーインスタンスをデータベースクライアントに提供します。

違いは、Pythonにはファーストクラスのタイプがあるということです。クラスを含むデータ型は、それ自体がオブジェクトです。特定のクラスを使用したい場合は、クラスに名前を付けるだけです。例えば:

if config_dbms_name == 'postgresql':
    import psycopg
    self.database_interface = psycopg
Elif config_dbms_name == 'mysql':
    ...

その後のコードは、次のように記述してデータベースインターフェイスを作成できます。

my_db_connection = self.database_interface()
# Do stuff with database.

JavaおよびC++が必要とする定型的なファクトリー関数の代わりに、Pythonは1行または2行の通常のコードでそれを行います。これが、関数型プログラミングと命令型プログラミングの強みです。

36
Daniel Newby

IoCとDIは、成熟したPythonコードでは非常に一般的です。アヒルのタイピングのおかげで、DIを実装するためのフレームワークは必要ありません。

最良の例は、settings.pyを使用してDjangoアプリケーションをセットアップする方法です。

# settings.py
CACHES = {
    'default': {
        'BACKEND': 'Django_redis.cache.RedisCache',
        'LOCATION': REDIS_URL + '/1',
    },
    'local': {
        'BACKEND': 'Django.core.cache.backends.locmem.LocMemCache',
        'LOCATION': 'snowflake',
    }
}

Django Rest FrameworkはDIを多用しています:

class FooView(APIView):
    # The "injected" dependencies:
    permission_classes = (IsAuthenticated, )
    throttle_classes = (ScopedRateThrottle, )
    parser_classes = (parsers.FormParser, parsers.JSONParser, parsers.MultiPartParser)
    renderer_classes = (renderers.JSONRenderer,)

    def get(self, request, *args, **kwargs):
        pass

    def post(self, request, *args, **kwargs):
        pass

思い出させてください( source ):

「依存性注入」は、5セントの概念を表す25ドルの用語です。 [...]依存性注入は、オブジェクトにインスタンス変数を与えることを意味します。 [...]。

30
Max Malysh

数年間Pythonを使用していませんが、他の何よりも動的に型付けされた言語であることに関係があると思います。 Javaの簡単な例では、何かが適切に標準出力に書き込まれたことをテストしたい場合、DIを使用して任意のPrintStreamを渡し、書き込まれているテキストをキャプチャして検証します。ただし、Rubyで作業している場合は、検証を行うためにSTDOUTの「puts」メソッドを動的に置き換えて、DIを完全に見えなくすることができます。抽象化を作成している唯一の理由が、それを使用しているクラスをテストすることである場合(ファイルシステム操作またはJavaのクロックを考えてください)、DI/IoCはソリ​​ューションに不要な複雑さをもたらします。

12
bcarlso

IoC/DIはデザインコンセプトですが、残念ながら特定の言語(またはタイピングシステム)に適用されるコンセプトとして採用されることがよくあります。 Pythonで依存性注入コンテナがはるかに普及するのを楽しみにしています。春がありますが、それはスーパーフレームワークであり、「[Java方法]」をあまり考慮せずにPythonの概念を直接移植したもののようです。

Python 3の注釈を考えると、フル機能を備えたシンプルな依存性注入コンテナー https://github.com/zsims/dic に亀裂を入れることにしました。これは、.NET依存性注入コンテナー(そのスペースでプレイしている場合はIMOが素晴らしい)からのいくつかの概念に基づいていますが、Python概念で変異しています。

9
zsims

依存性の注入と制御の反転が意味することを、人々は本当に理解していないことがわかりました。

制御の反転を使用するプラクティスは、別のクラスまたは関数に依存するクラスまたは関数を使用することですが、関数コードのクラスでインスタンスを作成する代わりに、パラメーターとしてそれを受け取る方がよいため、疎結合を実現できます。これには、よりテストしやすく、liskovの置換原理を実現するという多くの利点があります。

インターフェースとインジェクションを操作することで、コードのメンテナンス性が向上します。動作を簡単に変更できるためです。コードの1行(DI構成では1行または2行)を書き換える必要がないためです。クラスが待機しているインターフェイスを実装するクラスは、インターフェイスに従う限り、独立して変化する可能性があるため、その動作を変更するクラス。コードを切り離して維持しやすい状態に保つための最良の戦略の1つは、少なくとも1つの責任、置換、および依存関係の逆転の原則に従うことです。

オブジェクトをパッケージ内で自分でインスタンス化し、それをインポートして自分でインジェクトでき​​る場合、DIライブラリは何に適していますか? Javaには手続き型セクション(クラス外のコード)がないため、選択された答えは正しいです。すべては退屈な構成xmlになります。パフォーマンスを吹き飛ばすことはありませんが、pythonでは、コードの「プロシージャ」(クラス外のコード)セクションにインジェクションをコーディングするだけです。

8

実際には、DIを使用して十分にクリーンでコンパクトなコードを書くのは非常に簡単です(それでは/ stayPythonicですが、とにかく:)) 、たとえば、私は実際にこのコーディング方法を好みます:

def polite(name_str):
    return "dear " + name_str

def rude(name_str):
    return name_str + ", you, moron"

def greet(name_str, call=polite):
    print "Hello, " + call(name_str) + "!"

_

>>greet("Peter")
Hello, dear Peter!
>>greet("Jack", rude)
Hello, Jack, you, moron!

はい、これは単なる関数/クラスのパラメーター化の単純な形式と見なすことができますが、機能します。そのため、Pythonのデフォルトのバッテリーで十分かもしれません。

追伸 Pythonで単純なブールロジックを動的に評価する で、この素朴なアプローチのより大きな例を投稿しました。

7
mlvljr

私は「JörgW Mittag」の答えを返します。「DI/IoCのPython実装は非常に軽量であるため、完全に消滅します」。

この声明をバックアップするには、JavaからPythonに移植された有名なMartin Fowlerの例を見てください。 Python:Design_Patterns:Inversion_of_Control

上記のリンクからわかるように、Pythonの「コンテナ」は8行のコードで記述できます。

class Container:
    def __init__(self, system_data):
        for component_name, component_class, component_args in system_data:
            if type(component_class) == types.ClassType:
                args = [self.__dict__[arg] for arg in component_args]
                self.__dict__[component_name] = component_class(*args)
            else:
                self.__dict__[component_name] = component_class
6
emilmont

pythonの動的な性質のために、人々は別の動的なフレームワークの必要性をしばしば見ないと思います。クラスが新しいスタイルの「オブジェクト」から継承する場合、新しい変数を動的に作成できます( https://wiki.python.org/moin/NewClassVsClassicClass )。

i.e。普通のpythonの場合:

#application.py
class Application(object):
    def __init__(self):
        pass

#main.py
Application.postgres_connection = PostgresConnection()

#other.py
postgres_connection = Application.postgres_connection
db_data = postgres_connection.fetchone()

しかし、 https://github.com/noodleflake/pyioc をご覧ください。これはあなたが探しているものかもしれません。

i.e。pyiocで

from libs.service_locator import ServiceLocator

#main.py
ServiceLocator.register(PostgresConnection)

#other.py
postgres_connection = ServiceLocator.resolve(PostgresConnection)
db_data = postgres_connection.fetchone()
4

私の2セントは、ほとんどのPythonアプリケーションでは必要ないことであり、たとえ必要な場合でも、多くのJava嫌悪者(および開発者であると信じる無能なフィドラー)である可能性がありますJavaで人気があるからといって、それを何か悪いものと考えてください。

IoCシステムは、オブジェクトの複雑なネットワークがあり、各オブジェクトが他のオブジェクトの依存関係であり、それ自体が他のオブジェクトに依存している場合に実際に役立ちます。このような場合、これらのオブジェクトをすべて一度定義し、可能な限り多くの暗黙のルールに基づいて、それらを自動的にまとめるメカニズムが必要になります。アプリケーションのユーザー/管理者が簡単な方法で構成を定義する必要がある場合は、単純なXMLファイル(構成)からコンポーネントを読み取ることができるIoCシステムが必要な追加の理由です。

典型的なPythonアプリケーションは、そのような複雑なアーキテクチャなしで、はるかに単純で、単なるスクリプトの束です。個人的には、IoCが実際に何であるかを知っています(ここに特定の回答を書いた人とは反対です)限られたPython経験でそれの必要性を感じたことはありません(Springも使用していません)どこでも、それがもたらす利点が開発オーバーヘッドを正当化しない場合ではありません)。

そうは言っても、IoCアプローチが実際に役立つPython状況があり、実際、Djangoがそれを使用していることをここで読みました。

上記の同じ推論をJavaの世界のアスペクト指向プログラミングに適用できますが、AOPが本当に価値があるケースの数はさらに限られています。

3
zakmck