views:

893

answers:

6

In Java IoC / DI is a very common practice which is extensively used in web applications, nearly all available frameworks and Java EE. On the other hand, there are also lots of big Python web applications, but beside of Zope (which I've heard should be really horrible to code) IoC doesn't seem to be very common in the Python world. (Please name some examples if you think that I'm wrong).

There are of course several clones of popular Java IoC frameworks available for Python, springpython for example. But none of them seems to get used practically. At least, I've never stumpled upon a Django or sqlalchemy+<insert your favorite wsgi toolkit here> based web application which uses something like that.

In my opinion IoC has reasonable advantages and would make it easy to replace the django-default-user-model for example, but extensive usage of interface classes and IoC in Python looks a bit odd and not »pythonic«. But maybe someone has a better explanation, why IoC isn't widely used in Python.

+4  A: 

In my opinion, things like dependency injection are symptoms of a rigid and over-complex framework. When the main body of code becomes much too weighty to change easily, you find yourself having to pick small parts of it, define interfaces for them, and then allowing people to change behaviour via the objects that plug into those interfaces. That's all well and good, but it's better to avoid that sort of complexity in the first place.

It's also the symptom of a statically-typed language. When the only tool you have to express abstraction is inheritance, then that's pretty much what you use everywhere. Having said that, C++ is pretty similar but never picked up the fascination with Builders and Interfaces everywhere that Java developers did. It is easy to get over-exuberant with the dream of being flexible and extensible at the cost of writing far too much generic code with little real benefit. I think it's a cultural thing.

Typically I think Python people are used to picking the right tool for the job, which is a coherent and simple whole, rather than the One True Tool (With A Thousand Possible Plugins) that can do anything but offers a bewildering array of possible configuration permutations. There are still interchangeable parts where necessary, but with no need for the big formalism of defining fixed interfaces, due to the flexibility of duck-typing and the relative simplicity of the language.

Kylotan
Your first point is nonsense.
Finglas
Hehe, I like the link to the hammer-factory-factory story. Nice to read. :)
tux21b
@Finglas: Which part of it makes no sense? You may disagree with the premises but I think the logic is sound. It also ties with my experience on Python projects - you can swap parts out quickly and easily without any need for the formalised interfaces nor builders to inject one object inside of another.
Kylotan
@Kylotan: It isn't so much the framework as the language itself. To create the kind of flexibility that duck-typing languages enjoy, statically typed languages need very sophisticated frameworks and rules. DI is one of those rules. Python folks don't think twice about. Java folks have to really work at it.
S.Lott
@S.Lott - I'd totally agree with you, except that C++ people seem to get by without the explosion of design and architecture patterns, despite working with similar restrictions to those of Java. I think that implies a cultural difference where, upon being faced with 2 possible ways to do something, Java people prefer to extract another interface to facilitate the Strategy pattern whereas C++ people dip right in and add a bool and an if statement...
Kylotan
+4  A: 

Haven't used Python in several years, but I would say that it has more to do with it being a dynamically typed language than anything else. For a simple example, in Java, if I wanted to test that something wrote to standard out appropriately I could use DI and pass in any PrintStream to capture the text being written and verify it. When I'm working in Ruby, however, I can dynamically replace the 'puts' method on STDOUT to do the verify, leaving DI completely out of the picture. If the only reason I'm creating an abstraction is to test the class that's using it (think File system operations or the clock in Java) then DI/IoC creates unnecessary complexity in the solution.

bcarlso
+10  A: 

Part of it is the way the module system works in Python. You can get a sort of "singleton" for free, just by importing it from a module. Define an actual instance of an object in a module, and then any client code can import it and actually get a working, fully constructed / populated object.

This is in contrast to Java, where you don't import actual instances of objects. This means you are always having to instantiate them yourself, (or use some sort of IoC/DI style approach). You can mitigate the hassle of having to instantiate everything yourself by having static factory methods (or actual factory classes), but then you still incur the resource overhead of actually creating new ones each time.

TM
That makes sense. If I want to change an implementation in Python, I simply import from a different location using the same name. But now I am thinking if it's also possible the other way round by defining a `MyClassInstances` class to each `MyClass` in Java, which contains only static, fully initialized instances. That would be wired :D
tux21b
And another idea: Providing a way of changing such imports in python would it make it possible to replace implementations easily without touching all the python files. Instead of `from framework.auth.user import User` it might be better to write `User = lookup('UserImplentation', 'framework.auth.user.User')` (the 2nd parameter might be a default value) inside the framework. Then users of the framework would be able to replace/specialize the `User` implementation without touching the framework.
tux21b
@tux21b Using defaults to keep the code clean and at the same time providing the opportunity to supply (inject) any semantically correct dependency seems to be the _pythonic_ (or simpler, more natural) way, in my opinion too.
mlvljr
+16  A: 

I don't actually think that DI/IoC are that uncommon in Python. What is uncommon, however, are DI/IoC frameworks/containers.

Think about it: what does a DI container do? It allows you to

  1. wire together independent components into a complete application ...
  2. ... at runtime.

We have names for "wiring together" and "at runtime":

  1. scripting
  2. dynamic

So, a DI container is nothing but an interpreter for a dynamic scripting language. Actually, let me rephrase that: a typical Java/.NET DI container is nothing but a crappy interpreter for a really bad dynamic scripting language with butt-ugly, often XML-based, syntax.

When you program in Python, why would you want to use an ugly, bad scripting language when you have a beautiful, brilliant scripting language at your disposal? Actually, that's a more general question: when you program in pretty much any language, why would you want to use an ugly, bad scripting language when you have Jython and IronPython at your disposal?

So, to recap: the practice of DI/IoC is just as important in Python as it is in Java, for exactly the same reasons. The implementation of DI/IoC however, is built into the language and often so lightweight that it completely vanishes. (Here's an analogy: in assembly, a subroutine call is a pretty major deal - you have to save your local variables and registers to memory, save your return address somewhere, change the instruction pointer to the subroutine you are calling, arrange for it to somehow jump back into your subroutine when it is finished, put the arguments somewhere where the callee can find them, and so on. IOW: in assembly, "subroutine call" is a Design Pattern, and before there were languages like Fortran which had subroutine calls built in, people were building their own "subroutine frameworks". Would you say that subroutine calls are "uncommon" in Python, just because you don't use subroutine frameworks?)

BTW: for an example of what it looks like to take DI to its logical conclusion, take a look at Gilad Bracha's Newspeak Programming Language and his writings on the subject:

Jörg W Mittag
Many thanks for Newspeak hint, that man tells the truth :)
mlvljr
While I agree. The XML comment is wrong. Many (at least the modern) IOC containers use convention (code) over configuration (XML).
Finglas
There's nothing preventing you from writing the wiring explicitly in Java, but as you have more and more services, dependencies get more complex. A DI container is like Make: you declare the dependencies and the container initializes them in the right order. Guice is a Java DI framework where everything is written in Java code. By writing declaratively a DI container also adds support for post processing the declerations before initialization (e.g., replace property placeholders with actual values)
IttayD
+2  A: 

Django makes great use of inversion of control. For instance, the database server is selected by the configuration file, then the framework provides appropriate database wrapper instances to database clients.

The difference is that Python has first-class types. Data types, including classes, are themselves objects. If you want something to use a particular class, simply name the class. For example:

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

Later code can then create a database interface by writing:

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

Instead of the boilerplate factory functions that Java and C++ need, Python does it with one or two lines of ordinary code. This is the strength of functional versus imperative programming.

Daniel Newby
+1  A: 

Actually, it is quite easy to write sufficiently clean and compact code with DI (I wonder, will it be/stay pythonic then, but anyway :) ), for example I actually perefer this way of coding:

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

def rude(a):
    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!

Yes, this can be viewed as just a simple form of parameterizing functions/classes, but it does its work. So, maybe Python's default-included batteries are enough here too.

P.S. I have also posted a larger example of this naive approach at Dynamically evaluating simple boolean logic in Python.

mlvljr
For simple cases that might work, but just imagine a simple web blog controller, which uses various models (Post, Comment, User). If you want the user to inject his own Post model (with an additional viewcount attribute to track that), and his own User model with more profile information and so on, all the parameters might look confusing. Additionally, the user might want to change the Request object too, to support filesystem session instead of simple cookie based session or something like that... So, you will end up with lots of parameters shortly.
tux21b
@tux21b Well, there's "essential complexity" the users want the application to implement, there are architectural solutions to it (some of which are _not worse than the rest of them_ in terms of development and possibly maintenance time, exec. speed, etc.), and there's human ability to comprehend the API and software architecture. If there's no human-comprehensible solution at all (not just among those using (any form of) DI)... well, who said that all problems are solvable? And having lots of default-assigned (but swappable by user's choice) parameters may actually suffice often.
mlvljr