Python中的依赖注入容器是否有意义?

时间:2011-07-30 03:24:58

标签: python dependency-injection

一直在搞乱python,像往常一样,它把我刚性的静态类型面向对象的世界抛到了一个混乱的地方。 Python支持duck typing,没有基于接口的编程的可用概念(如在C#接口中)并允许全局变量。有了所有这些好东西,依赖注入容器真的有任何意义,或者Python运行时成为容器。

我理解这些容器在静态类型的OO语言中的意义,例如Java和C#但是这样的东西会融入python的坚果世界(我喜欢它)吗?

我一直怀疑依赖注入作为一种设计模式是一种难闻的气味,一切都必须是一个“纳粹思维”的类,即c#和Java,我会是正确的还是我缺少的东西?

到目前为止,我认为只需使用Globals就可以覆盖工厂,Singletons,多实例对象。我也怀疑Aspect的内容也被覆盖了,虽然我还在考虑这个问题。

鸭子打字是目前让我感觉到的东西,因此用于定义接口然后在这些接口上基于类,并让静态的东西覆盖我的愚蠢,我觉得没有静态类型,容器有点无用。

修改

我认为在使用python时我不会使用Dependency Injector框架/容器。真的没有任何意义。在思考并阅读到目前为止的反馈之后,论证清楚地表明,如果没有静态类型定义,所作出的承诺是如此宽松,以至于为什么要麻烦。鸭子打字就是这样,唯一的承诺可以通过文档来实现。只要引用通过signiture参数进入类Method / function,而不是通过编程环境的以太,那么我认为我会安全。

但令人震惊的是,我无法通过我在Java和C#中完成的过度设计实践来强制执行我对其他人的意愿。我在乎...... nah :)

2 个答案:

答案 0 :(得分:13)

  

没有基于接口的编程的可用概念(如在C#接口中)

仅仅因为编译器无法检查您是否正确使用了接口并不意味着“没有可用的接口概念”。您记录一个接口,并编写单元测试。

对于全局变量,它不像public static方法和C#或Java类上的字段真的有任何不同。例如,考虑java.lang.Math的工作原理。现在考虑一下java.lang.Math 不是一个Singleton的事实。他们这样做是有充分理由的。

  

有了所有这些好东西,依赖注入容器确实有任何意义

我对此表示怀疑,但我从未真正在C#或Java中看到过这一点。在我看来,依赖注入是一种编程技术。而且,它也没有那么多。

  

我一直怀疑依赖注入作为一种设计模式是一种难闻的气味,一切都必须是“纳粹思想”所创造的

不,不是。依赖注入在很多时候都是一个好主意。您也不需要一个类来注入依赖项。每次将某些东西作为参数传递给自由函数时,不是让函数调用另一个函数来获取信息,而是基本上做同样的事情:控制反转。 Python还允许您以多种方式处理类似于类的模块(当然比Java和C#更多的方式)。将模块作为参数传递给函数可以解决一些问题。 :)

  

到目前为止,我认为只需使用Globals就可以覆盖工厂,Singletons,多实例对象。

单身者是难闻的气味,如果有的话。在几乎所有情况下,根据我的丰富经验,它们的存在是因为有人认为原则上Bad(TM)具有全局性,没有真正考虑可能的选项,或者为什么他们想要对单个共享对象的那种访问,或者甚至为什么全局变量首先是“原则上的坏(TM)”。

可以在Python中创建一个充当工厂的全局函数。但是,我会说更多Pythonic可以执行以下任何操作:

a)首先,真的,真的,真的确保你不能只用__init__做你想做的事。我的意思是,在一种动态类型语言中,你可以通过这种方式做很多事情。

b)如果__init__不会删除它,请尝试使用__new__来控制行为。

在Python中,类本身就是对象,可以调用。默认情况下,调用它们会实例化该类。使用__new__,您可以加入其中。

c)使用应用于该类的装饰器。这是一个制作Singleton的例子(仅仅因为):

def _singleton(cls):
  instance = cls()
  result = lambda: instance
  result.__doc__ = cls.__doc__
  return result

@_singleton
class example(object): pass

这样做的方式:当你装饰类时,调用_singleton(),传入类。构造并缓存一个实例,_singleton()返回一个匿名函数,它将返回调用时的实例。要完成这个游戏,该类的文档将附加到匿名函数。然后Python将全局范围内的类名称重新绑定到返回的匿名函数。因此,当您调用它时,每次都会得到相同的类实例。

现在,这仍然可以解决,当然(你可以做example().__class__()之类的事情来获得另一个实例),但是你做错了比你简单地忽略一个工厂要清楚得多函数以便正常使用构造函数。另外,这意味着调用代码实际上就像正常调用构造函数一样:)

  

鸭子打字是目前让我感觉到的东西,因此用于定义接口然后在这些接口上基于类,并让静态的东西覆盖我的愚蠢,我觉得没有静态类型,容器有点无用。

你需要改变你的想法:不要担心你已经通过的事情,并担心是否可以做你想做的事 。这就是鸭子打字的方式。

答案 1 :(得分:7)

结论

(以下是原帖中最相关的部分。我承认,我有点诗意,所以我虽然应该在他们自己的部分中包含最重要的句子。那就说,我觉得这是诗意的打蜡很重要,我没有删除它。)

仍然使用依赖注入。始终需要对象与已经实例化的对象进行通信。需要“父”对象(或容器,或其他)来设置其“子”的状态。这些需要通过某种方法传递或通过某种赋值来设置,但从抽象意义上说,它是相同的。

原答案:

键入系统

从许多方面来说,Python是我遇到过的第二个数学上最纯粹的语言 - 它只遵循Scheme(虽然我从未使用过Haskell,但我知道它也会在那里)。

Python本身支持闭包,它具有continuation(yield语法),多重继承和循环理解语法,这在很大程度上是无与伦比的。所有这些都使它更接近Alonzo Church在Lambda微积分中的原始愿景(麦卡锡在Lisp背后的原创思想)。 Python 3通过集合理解使其变得更加纯粹(这让我的心灵只是在思考它们固有的美感)。

持续不断地存在这样的想法:存储在Python变量中的数据与数学中的对应物有更多共同之处,以至于对象的界面可以简化为“形容词或形容词”描述对象的形容词“。基本上,对象的界面完全包含在__dict__中,并与dir一起显示。

考虑到所有这一切,它确实开始让人怀疑看待事物的“传统”方式(带有引号的“传统”方式,因为Python与Java一样旧)是否真的可以在这种语言中工作。甚至Python中的参数列表对他们都有OOP感觉(kwargs,任何人?)它确实会让世界颠倒过来。

仍然使用依赖注入。始终需要对象与已经实例化的对象进行通信。需要“父”对象(或容器,或其他)来设置其“子”的状态。这些需要通过某种方法传递或通过某种赋值来设置,但从抽象意义上说,它是同一个东西。但是有一种隐含的信任,即传递的对象的__dict__的内容将包含适当的描述。因此,它变得更少了,“一旦我实例化了这个物体,我将赋予它生命所需要的东西,”而且更多,“嗯,是的,一个物体需要一个状态,所以我是给它一个。“

这暴露了静态类型的隐藏方面。为了让期望IFoo能够工作的东西,它必须完全和完整地了解IFoo的含义,即使它永远不需要90%的定义。同时,duck typing使得依赖对象只知道属性X,Y和Z应该在运行时存在。

全局

关于全局变量。除非你别无选择,否则不要使用它们。如果只是因为Singletons允许您在值更改时进行记录,那么最好使用Singleton。全局变量不是这样。

有一个编程规则,你的界面越暴露和复杂,维护起来就越困难。如果在全局变量中放置了某些内容, 模块中的任何可能会修改 模块中的任何内容价值。代码几乎可以变得不确定。我向你保证,悲伤随之而来。