Python 中有依赖注入容器吗?
一直在摆弄 python,像往常一样,它让我严格的静态类型面向对象世界变得有点混乱。 Python 支持鸭子类型,没有基于接口的编程的可用概念(如 C# 接口)并允许全局变量。有了所有这些好处,依赖注入容器真的有任何意义吗?或者 Python 运行时是否会成为容器。
我理解这些容器在静态类型 OO 语言(例如 Java 和 C#)中的意义,但是这样的东西如何适合 Python 的疯狂世界(我喜欢它)?
我一直怀疑依赖注入作为一种设计模式是一种难闻的气味,一切事物都必须是 C# 和 Java 的“纳粹思维”类,我是正确的还是有什么我遗漏的?
到目前为止,我认为我可以通过使用全局来涵盖工厂、单例、多实例对象。我还怀疑 Aspect 的内容也被涵盖了,尽管我仍在考虑这一点。
Duck Typing 是目前让我着迷的东西,所以习惯于定义接口,然后在这些接口上建立类,并让静态的东西掩盖我的愚蠢,我觉得没有静态类型,容器有点无用。
Been messing about with python, as usual it throws my rigid static typed Object Oriented world in to a bit of a mess. Python supports duck typing, has no usable concept of interface based programming (as in C# interfaces) and allows Global variables. With all these goodies is there really any point to a dependency injection container or does the Python run-time become the container.
I understand the point to these containers in static typed OO languages such as Java and C# but where would such a thing fit into the nutty world of python (I love it)?
I have always suspected that Dependency injection as a design pattern was a bad smell that has been created by everything must be a class "Nazi thinking" that is c# and Java, would i be correct or is there something I am missing?
So far I think I can cover factories, Singletons, Multi-instance objects, just by using Globals. I also suspect that the Aspect stuff is covered too, although I am still thinking about this.
The Duck Typing is the thing that is getting me at the moment, so used to defining interfaces then basing classes on these interfaces and letting the static stuff cover my stupidity that I feel that without static typing, containers are a bit useless.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
仅仅因为编译器无法检查您是否正确使用接口并不意味着“没有可用的接口概念”。您记录接口并编写单元测试。
至于全局变量,它与 C# 或 Java 类上的
public static
方法和字段没有什么不同。例如,考虑 java.lang.Math 的工作原理。现在考虑一下 java.lang.Math 不是 Singleton 的事实。他们这样做是有充分理由的。,我对此表示怀疑,但我也从未真正在 C# 或 Java 中看到它们的意义。在我看来,依赖注入是一种编程技术。而且实际上也没有那么多。
,不,不是。很多时候依赖注入是一个好主意。您也不需要一个类来注入依赖项。每次您将某些内容作为参数传递给自由函数时,而不是让该函数调用另一个函数来获取信息,您基本上都在做同样的事情:控制反转。 Python 还允许您在很多方面将模块视为类(当然比 Java 和 C# 的方式更多)。有些问题可以通过将模块作为参数传递给函数来解决。 :)
如果有的话,单身人士就是难闻的气味。根据我的丰富经验,几乎在每种情况下,它们的存在都是因为有人认为原则上拥有全局变量是不好的,而没有真正考虑可能的选项,或者为什么他们想要对单个共享对象进行这种访问,或者甚至是为什么全局变量从一开始就是“Bad(TM)”。
您可以在Python中创建一个充当工厂的全局函数。然而,我想说,做以下任何事情都更Pythonic:
a)首先,真的,真的,真的确保你不能只是用
__init__。我的意思是,在动态类型语言中,您可以通过这种方式做很多事情。
b) 如果
__init__
不能解决问题,请尝试使用__new__
来控制行为。在Python中,类本身就是对象,是可调用的。默认情况下,调用它们会实例化该类。使用
__new__
,您可以连接到它。c) 使用应用于类的装饰器。下面是一个创建 Singleton 的示例(只是因为):
工作方式:当您装饰类时,调用
_singleton()
并传入该类。构造并缓存一个实例,并且_singleton()
返回一个匿名函数,该函数在调用时将返回实例。为了完成这个谜题,类的文档被附加到匿名函数上。然后Python将全局范围内的类名重新绑定到返回的匿名函数。因此,当您调用它时,每次都会获得该类的相同实例。现在,当然,这仍然可以解决(您可以执行诸如
example().__class__()
之类的操作来获取另一个实例),但更明显的是,您做错了事情,而不是如果您只是为了正常使用构造函数而忽略了工厂函数。另外,这意味着调用代码实际上就像正常调用构造函数一样:)你需要转变你的思维:不要再担心你传递给你的东西是什么,而是担心它是否能做你想让它做的事情。这就是鸭子打字的工作原理。
Just because the compiler can't check that you're using the interface correctly doesn't mean there's "no usable concept of interfaces". You document an interface, and write unit tests.
As for globals, it's not like
public static
methods and fields on C# or Java classes are really any different. Consider how java.lang.Math works, for example. Now consider the fact that java.lang.Math isn't a Singleton. They did that for a good reason.I doubt it, but then I never really saw the point of them in C# or Java, either. Dependency injection is a programming technique, in my view. And there's really not that much to it, either.
No, it isn't. Dependency injection is a good idea a lot of the time. You don't need a class to inject dependencies into, either. Every time you pass something to a free function as a parameter, instead of having the function call another function to get the information, you're basically doing the same thing: inversion of control. Python also lets you treat modules similarly to classes in a lot of ways (certainly more ways than Java and C# do). There are problems that can be solved by passing modules as parameters to functions. :)
Singletons are the bad smell, if anything. In nearly every case, in my extensive experience, they exist because someone thought it would be Bad(TM) on principle to have a global, without really thinking through the possible options, or why they wanted that kind of access to a single shared object, or even why globals are "Bad(TM) on principle" in the first place.
You could make a global function in Python that acts as a factory. However, I would say it's more Pythonic to do any of the following:
a) first, make really, really, really sure you can't just do what you want with
__init__
. I mean, in a dynamically typed language, you can do a heck of a lot that way.b) If
__init__
won't cut it, try using__new__
to control the behaviour.In Python, classes are objects themselves, which are callable. By default, calling them instantiates the class. With
__new__
, you can hook into that.c) Use a decorator applied to the class. Here is an example that makes a Singleton (just because):
The way this works: when you decorate the class,
_singleton()
is called, with the class being passed in. An instance is constructed and cached, and_singleton()
returns an anonymous function that will return the instance when called. To complete the charade, the class's documentation is attached to the anonymous function. Then Python rebinds the class' name in the global scope to the returned anonymous function. So when you call it, you get the same instance of the class, every time.Now, this can still be worked around, of course (you can do something like
example().__class__()
to get another instance), but it's much more clear that you're doing something wrong than if you simply ignored a factory function in order to use the constructor normally. Plus, it means the calling code actually acts as if it were calling the constructor normally :)You need to shift your thinking: stop worrying about what the thing you've been passed is, and worry about whether it can do what you want it to do. That's how duck typing works.
结论
(以下是原帖中最相关的部分。我承认,我有点诗意了,所以我想我应该简单地将最重要的句子包含在他们自己的部分中。也就是说,我觉得诗意的增加非常重要,我还没有删除它。)
依赖注入仍然被使用。对象总是需要与已经实例化的对象进行通信。需要“父”对象(或容器,或其他)来设置其“子”的状态。这些需要通过某种方法传入或通过某种赋值进行设置,但从抽象意义上来说,它们是同一件事。
原始答案:
类型系统
从很多方面来说,Python 是我遇到过的第二个最纯粹的数学语言——它只遵循Scheme(虽然我从未使用过 Haskell,但我知道这一点也很高)。
Python 原生支持闭包,它具有延续(yield 语法)、多重继承和循环理解语法,这在很大程度上是无与伦比的。所有这些使它更接近 Alonzo Church 在 Lambda Calculus 中的最初设想(麦卡锡在 Lisp 背后的最初想法)。 Python 3 通过集合推导式使其变得更加纯粹(一想到它们与生俱来的美就让我心潮澎湃)。
人们不断地认为,存储在 Python 变量中的数据与数学中的对应数据有更多共同点,以至于对象的接口可以简化为简单的“形容词或形容词集,它们描述了对象”。基本上,对象的接口完全包含在其
__dict__
中,并通过dir
显示。考虑到所有这些,它确实开始让人怀疑“传统”的看待事物的方式(“传统”加引号是因为 Python 和 Java 一样古老)是否真的可以在这样一种语言中工作。当 Python 中的参数列表也有 OOP 的感觉时(kwargs,有人吗?),它确实把世界颠倒了。
仍然使用依赖注入。对象总是需要与已经实例化的对象进行通信。需要“父”对象(或容器,或其他)来设置其“子”的状态。这些需要通过某种方法传入或通过某种赋值进行设置,但从抽象意义上来说,它们是同一件事。但存在一种隐含的信任,即传递的对象的 __dict__ 的内容将包含适当的描述。正因为如此,它变得少得多,“一旦我实例化了这个对象,我就会赋予它生命所需的东西”,而更多的是,“嗯,是的,一个对象需要一个状态,所以我给它一个。”
这暴露了静态类型的一个隐藏的方面。为了让需要
IFoo
的东西能够工作,它必须完全了解IFoo
的含义,即使它永远不需要 90%该定义的。同时,鸭子类型使依赖对象只知道属性 X、Y 和 Z 应该在运行时存在。全局变量
至于全局变量。除非您别无选择,否则请避免使用它们。您最好使用单例,因为单例允许您在值更改时进行记录。对于全局变量来说情况并非如此。
编程有一条规则,界面越暴露、越复杂,维护起来就越困难。如果某些内容被放置在全局变量中,则该模块中的任何,以及ANY模块中的任何都可以修改值。代码几乎可能变得不确定。我向你保证,悲伤随之而来。
The conclusion
(The following is the most relevant part of the original post. I admit, I waxed a little poetical, and so I though I should simply include the most important sentences in their own section. That said, I feel that the poetic waxing is important enough that I have not deleted it.)
Dependency injection is still used. There will always be the need for objects to communicate with already instantiated objects. There will be a need for "parent" objects (or containers, or whatever) to set up the state of their "children". These will need to be passed in through some method or set through some assignment, but in the abstract sense, it is the same thing.
The original answer:
Type systems
Python is, in many ways, the second most mathematically pure language I've ever encountered -- it follows only Scheme (though I've not ever used Haskell, I understand that to be up there too).
Python supports closures natively, it has continuations (yield syntax), multiple inheritance, and a loop comprehension syntax which is largely unparalleled. All of these bring it far closer to Alonzo Church's original vision in Lambda Calculus (an McCarthy's original ideas behind Lisp). Python 3 makes it even more pure with set comprehensions (which make my heart flutter just thinking of their inherent beauty).
Constantly and continually there is the idea that the data stored in the variables of Python have more in common with their counterparts in Mathematics, so much that the interface of an object can be diminished to simply be, "the adjectives or set of adjectives which describe the object". Basically, an object's interface is wholly and completely contained in its
__dict__
and displayed withdir
.All of that considered, it does start making one wonder whether the "traditional" way of looking at things ("traditional" with quotes because Python is as old as Java) really can work in such a language. When even arguments lists in Python have an OOP feeling to them (kwargs, anyone?) it really does turn the world upside-down.
Dependency injection is still used. There will always be the need for objects to communicate with already instantiated objects. There will be a need for "parent" objects (or containers, or whatever) to set up the state of their "children". These will need to be passed in through some method or set through some assignment, but in the abstract sense, it is the same thing. But there is an implicit trust that the contents of the passed object's
__dict__
will contain an appropriate description. Because of this, it becomes far less, "once I have instantiated this object, I will bestow upon it the very things needed for the life," and far more, "Well, yea, an object needs a state, so I'm giving it one."And this exposes a hidden aspect of static typing. In order for something which expects an
IFoo
to work, it has to have full and complete knowledge of what it means to be anIFoo
, even if it will never need 90% of that definition. Meanwhile, duck typing makes the depending object know only that properties X, Y, and Z should be there at run-time.Globals
As to globals. Avoid them unless you have no other choice. You are better off using a Singleton if only because Singletons allow you to log when the value changes. This is not true of globals.
There is a rule of programming, the more exposed and complicated your interface, the harder it will be to maintain. If something is placed in a global variable, anything in that module, and possibly anything in ANY module can modify the value. The code can almost become non-deterministic. I assure you, sadness follows.