在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中没有被广泛使用。


当前回答

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

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

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

其他回答

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

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

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

部分原因是模块系统在Python中的工作方式。你可以免费获得一种“单例”,只需从模块中导入它。在模块中定义一个对象的实际实例,然后任何客户端代码都可以导入它,并实际获得一个工作的、完全构造/填充的对象。

这与Java相反,在Java中不导入对象的实际实例。这意味着你总是必须自己实例化它们(或使用某种IoC/DI风格的方法)。您可以通过使用静态工厂方法(或实际的工厂类)来减轻必须自己实例化所有东西的麻烦,但这样您仍然会产生每次实际创建新方法的资源开销。

实际上,使用DI编写足够干净和紧凑的代码是相当容易的(我想知道,它会/保持python化吗,但无论如何:)),例如,我实际上更喜欢这种编码方式:

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中动态计算简单的布尔逻辑。

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美元术语。[…依赖注入意味着给一个对象它的实例变量。[…]。

我支持“Jörg W Mittag”的回答:“DI/IoC的Python实现是如此的轻量级,以至于它完全消失了”。

为了支持这一说法,看看Martin Fowler从Java移植到Python的著名示例:Python:Design_Patterns:Inversion_of_Control

从上面的链接中可以看到,Python中的“Container”可以用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