在Java中,IoC / DI是一种非常常见的实践,广泛应用于web应用程序、几乎所有可用的框架和Java EE中。另一方面,也有很多大型的Python web应用程序,但除了Zope(我听说它的编码真的很糟糕)之外,IoC在Python世界中似乎并不常见。(如果你认为我是错的,请举一些例子)。

当然,有一些流行的Java IoC框架的克隆可用于Python,例如springpython。但它们似乎都没有被实际使用。至少,我从来没有碰到过Django或sqlalchemy+<插入您最喜欢的wsgi工具箱在这里>的基于web应用程序使用类似的东西。

在我看来,IoC有合理的优势,可以很容易地取代django-default-user-model,但在Python中广泛使用接口类和IoC看起来有点奇怪,而且不»pythonic«。但是也许有人有更好的解释,为什么IoC在Python中没有被广泛使用。


当前回答

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只需要一两行普通代码就可以完成。这就是函数式编程与命令式编程的优势所在。

其他回答

所有基于DI的pytest夹具(来源)

在我看来,像依赖注入这样的事情是僵化和过于复杂的框架的症状。当代码主体变得过于沉重而难以更改时,你会发现自己不得不选择其中的一小部分,为它们定义接口,然后允许人们通过插入这些接口的对象来改变行为。这很好,但最好在一开始就避免这种复杂性。

这也是静态类型语言的症状。当您必须表达抽象的唯一工具是继承时,那么您几乎到处都在使用继承。话虽如此,c++与之非常相似,但从未像Java开发人员那样对构建器和接口着迷。人们很容易以编写太多泛型代码为代价,实现灵活和可扩展的梦想,而这些代码几乎没有真正的好处。我认为这是文化问题。

通常我认为Python人习惯于为工作选择正确的工具,这是一个连贯而简单的整体,而不是一个真正的工具(带有一千种可能的插件),它可以做任何事情,但提供了令人眼花缭乱的可能配置排列。在必要的地方仍然有可互换的部分,但由于duck-typing的灵活性和语言的相对简单性,不需要定义固定接口的大形式主义。

IoC和DI在成熟的Python代码中非常常见。由于鸭子类型,您不需要框架来实现依赖注入。

最好的例子是如何使用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

让我提醒一下(来源):

“依赖注入”是一个5美分概念的25美元术语。[…依赖注入意味着给一个对象它的实例变量。[…]。

IoC容器主要使用**kwarg来“模仿”

class A:
    def __init__(self, **kwargs):
        print(kwargs)

Class B:
    pass

Class C:
    pass

Ainstance = A(b=B, c=C)

你可以手动使用Python进行依赖注入,但手动方法有其缺点:

大量的样板代码来进行连接。你可以使用Python的动态特性来进行注入,但这样你就失去了IDE的支持(例如,PyCharm中的Ctrl+Space),并且你会使代码更难理解和调试 没有标准:每个程序员都有自己解决相同问题的方法,这导致了重新发明轮子,理解彼此的代码很快就会成为一种痛苦。依赖注入库为插件提供了简单的框架

要做到这一切,我们需要一个依赖注入框架,例如这个https://python-dependency-injector.ets-labs.org/index.html似乎是Python最成熟的依赖注入框架。

对于较小的应用程序,DI容器是不必要的,对于任何有几百行代码或更多的东西,DI容器是必须的,以保持你的代码可维护性。