423

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.

alexandrul
  • 12,856
  • 13
  • 72
  • 99
tux21b
  • 90,183
  • 16
  • 117
  • 101
  • 3
    My guess, same reason that it is less popular in Ruby, built-in mixins and open classes – Sam Saffron Mar 17 '10 at 11:20
  • 3
    you ever tried springpython? it doesn't even work as advertised. at least in the aop portion. everything else in there is not very useful unless you are coming from java and need some level of comfort during the transition. – Tom Willis Mar 17 '10 at 17:47
  • 14
    Please take care to distinguish between the use of DI, and the use of an IOC framework. The former is a design pattern, the latter is a framework to assist in the automated use of the former. – Doug Nov 20 '11 at 14:30
  • Doug, I believe you meant to say DI is the creational feature that is obtained by using the Decorator pattern. – njappboy Mar 05 '14 at 00:45
  • At this moment, there is no working link with best practices. – bm13kk Jan 23 '15 at 11:43
  • 11
    I'd love to see an answer that addresses the real world problems that DI solves: Lifetime management, ease of test stubbing, etc. If there's a more Pythonic way to tackle these, I'm all ears. – Josh Noe Jun 29 '18 at 00:35
  • 3
  • "In my opinion IoC has reasonable advantages": Could you add an example where IoC would have helped you in Python and also describe how it would have helped / where the problem was? – Martin Thoma Jun 11 '20 at 18:24
  • I can. I'm contributing to a distributed reinforcement learning framework. IoC is a perfect fit for our case, as there are tons of different components that are built up as a direct result of parsing a declarative "experiment" config file. We want that config to be highly extensible, and there's pretty much no way to achieve that with a direct control design (that's how the initial version was written). Project is at https://github.com/AechPro/distrib-rl if you want to see what I mean (look for `cfg`/`config` dicts all over the place ). – Ben Burns Aug 18 '22 at 09:06

18 Answers18

229

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, sometimes 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 a brief aside for 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:

Community
  • 1
  • 1
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • 78
    While I agree. The XML comment is wrong. Many (at least the modern) IOC containers use convention (code) over configuration (XML). – Finglas May 19 '10 at 09:32
  • 25
    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 Oct 05 '10 at 13:50
  • 190
    "The implementation of DI/IoC however, is built into the language and often so lightweight that it completely vanishes." Down vote because this is categorically untrue. DI is a pattern where an interface is passed in to the constructor. It is not built-in in python. – Doug Nov 20 '11 at 14:33
  • 196
    downvote, wiring together has nothing to do with scripting, DI is a pattern, and it is not equivalent to scripting – Luxspes Dec 23 '12 at 06:06
  • 54
    I disagree with this. DI doesn't solve the lack of dynamic scripting in static languages. It provides a framework for configuring and composing your application's parts. I once heard a Ruby dev say that DI is unnecessary in dynamic languages. But he used Rails... Rails is just a big DI container of sorts, which uses convention to figure out which parts to configure when. He didn't need DI because Rails solved the problem of finding the parts for him. – Brian Genisio Sep 25 '13 at 09:56
  • 17
    downvote, "wiring together" and "at runtime" is kinda correct but "scripting" and "dynamic" are wrong analogies! – Dmitry Dec 28 '13 at 05:46
  • 6
    I dare say this is the best answer I have seen on StackExchange, wrong or right doesn't matter, but the combination of the answer+comments here gives the best understanding of DI even better than a direct question: what is DI? This is unfortunate though I can not up-vote because it's an indicator of how reliable an answer is (I guess!). – hkoosha Sep 03 '14 at 16:31
  • 1
    @Doug Don't agree. In Django, for example, you just can have global setting, that stores a string with namespace and class name for something (authentication backend or whatever). And if some code want to use this backend, it goes to that setting and imports in run-time this class by the reference stored in the setting. That solves exactly the same problem as DI. *Daniel Newby* gave the example of that in his answer. – Gill Bates Jun 25 '15 at 10:47
  • 23
    @GillBates I down voted this because injecting dependencies is *not* under *any circumstances* 'built into the language'. Python barely even has the concept of an interface without abc or zope.interface; the suggestion that it has an *inbuilt* solution for injecting *interface implementations with recursive dependencies* into a dependent class is just wrong. The sentiment in the answer may be valid, but that doesn't make the answer right when it says something which is wrong. – Doug Jun 25 '15 at 13:53
  • 5
    @GillBates and others. Dependency injection means giving an object its instance variables. The fact that you can statically bind interfaces into the object's method that instantiates it, serves a purpose for better lookup and stronger support for "programming to an interface". The answer is really correct. Dynamic language and duck typing is nothing but injecting dependencies at the runtime as the object knows nothing about the caller. Some python developers however practice documenting 'interfaces' in docstrings, so you could also have an IDE support for programming to an interface. – flackjap Sep 11 '15 at 03:20
  • I cannot edit my previous comment. It was intended for @Doug Nevertheless, it is really a different mindset when you want to architecture something in an enviroment that doesn't have static bindings during the compilation. You need to run through hoops and over hurdles in, for example, Java, if you want to get something simple as module loader. Codebase in Python tends to be a lot smaller for that reason, thus making refactoring or testing plain change of 'imports'. It's also very trivial to simulate DI/IoC architecture in Python if you only want to wire components in one central place. – flackjap Sep 11 '15 at 15:44
  • 2
    While the traditional way is to associate A object to B object from A object itself, the key idea of DI is to sepearte this wiring somewhere else. Whatever scripting, XML or runtime you are talking about is not the point. – Jason Ching Oct 06 '16 at 12:59
  • 5
    Downvoted, incorrect. DI also controls lifetime, when object gets constructed and destructed plus additional clean up. Javascript as a dynamic language also has a ton of DI frameworks. Angular didn't have proper DI (more of a AMD loader) and they had to fix that in Angular 2.0. – Alwyn Feb 23 '17 at 19:02
  • 3
    Downvoted. DI is not just a manifestation of runtime checks, it is a design decision. There is nothing preventing a python programmer to instantiate objects **_inside_** a method, thus coupling them to the method, making them non-injectable. I don't believe DI is built-in into python, although I do agree that duck typing helps cleaning up a lot of the DI cruft common in java. – Abhishek Pathak May 18 '17 at 13:35
  • 1
    Downvoted. A DI container does not, as stated in your post, wire anything together. It just holds different objects. A Injector uses the container later when objects of a specific "type" are requested. – Oskar Aug 16 '17 at 15:09
  • 1
    There is no such thing as "scripting language". – BartoszKP Sep 12 '17 at 21:01
  • 24
    Downvoted. Too much babbling about how wonderful the language is and yet the response about the aforementioned pattern is fundamentally wrong. – marcio Nov 21 '17 at 03:28
  • 9
    Based apparently on a fundamental misunderstanding of what DI is, and what DI frameworks typically do. Sorry to see such a biased, contentless answer voted as correct. – ggranum Jan 02 '18 at 16:35
  • 4
    Still waiting for python to inject my dependencies. – andho Mar 20 '18 at 07:42
  • 8
    downvote, opinionated rant that and does not really answer the question. Of course you can write your own IOC container, that doesn't mean you always want to do it – nixon Apr 25 '18 at 23:13
  • 5
    Downvoted, but not entirely for the reasons stated above. The response fails to justify it's premises, or does so so badly that I and evidently many other cannot tell. Specifically the claims that DI is a scripting interpreter and that DI is built into the language need more support than what is provided. Downvote will be removed upon edit with better support and code examples showing how python fulfills the requirements of modern DI containers in other langues. – Benjamin Jun 26 '18 at 16:10
  • 2
    Code examples please? – Josh Noe Jun 29 '18 at 00:13
  • 19
    What an arrogant answer. If you instantiate an object, regardless of the language, that object may have dependencies. Someone has to provide these dependencies. DI frameworks centralize this using containers. "DI is an interpreter" What? – kovac Jul 11 '18 at 05:54
  • 10
    Downvote, this answer is completely misleading and should be un-accepted. Dependency injection is mainly meant to allow inversion of control - I don't want to know in a caller class, which implementation of the callee I need. Is like having an API that connects to a DB. I don't want to instantiate a PostgresDBWriter class in every instance, I want to use a DBWrite to allow changing the implemention to Mysql for example. Being dynamic has nothing to do with DI - javascript frameworks also use DI even though js is also dynamic. – Avi Aug 06 '18 at 05:54
  • 10
    Surprised the number of upvotes, considering the answerer has got DI completely wrong. I'm sad. – Prathap Oct 09 '18 at 09:25
  • I mean yeah, maybe 8 years ago Jorg had that understanding of DI, and maybe containers sucked at that time, but we can't be angry at people that don't know better. Things evolve, and hopefully readers will look at the comment and understand the wrong in this answer. – Félix Adriyel Gagnon-Grenier Dec 12 '18 at 19:44
  • It's unfortunate the author of the accepted answer doesn't/didn't understand what DI is or why you'd need inversion of control in enterprise software. – Decker Feb 12 '19 at 19:43
  • 2
    Downvote as telling that something is beautiful or ugly without providing any insides or references. The answer is more like a right-wing political than technical. – Vladimir Vukanac Mar 25 '19 at 14:01
  • This is 2020 and I still don't get what DI Python uses. Or if any. I vaguely get from this and other answers is that Python doesn't use it, but Django does. It's just like what one comment puts it, Ruby doesn't use DI...without exactly knowing that Rails is a big giant DI container. – inmyth Nov 14 '20 at 08:11
  • Dependency injection is not only dynamic behavior (common in languages like Python or JavaScript). It also gives possibility to close resources (files, socket, temporary directories, databases) - something like with statement, but expanding through many methods and classes. Pytest fixtures actually implement this, unfortunately it can't be used outside pytest. – iirekm Dec 14 '20 at 08:57
  • Dependency injection is not about any Xmls (see micronaut or modern versions of spring), it's also not about being dynamic (see micronaut or dagger - most of job done at compile time). Dependency injection is about providing dependencies without worrying how to instantiate them, and about life cycle management (eg closing database connections on exit) and doing any large project without dependency injection, in any language, is a hassle – iirekm Dec 21 '20 at 13:09
  • DI is not an xml file nor a scipting language. It's a programming pattern that you seem to be misunderstanding – Neistow Jul 13 '21 at 22:01
88

IoC and DI are super common in mature Python code. You just don't need a framework to implement DI thanks to duck typing.

The best example is how you set up a Django application using settings.py:

# 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 utilizes DI heavily:

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

Let me remind (source):

"Dependency Injection" is a 25-dollar term for a 5-cent concept. [...] Dependency injection means giving an object its instance variables. [...].

Max Malysh
  • 29,384
  • 19
  • 111
  • 115
  • 12
    +1. Well put. Being a Python programmer I was completely baffled by a whole interview presentation on DI frameworks in C#. Took me a while to realise I already did it all the time in Flask apps without even thinking about it because you don't need a framework. To someone who doesn't know anything beyond C#/Java the question makes sense. To duck-typed language programmers it is just natural and as you say, "25-dollar term for a 5-cent concept". – Samuel Harmer Apr 04 '19 at 08:13
  • 26
    err... this is not dependency injection as the instances (`IsAuthenticated`, `ScopedRateThrottle`) are instantiated by the class. They are not passed into the constructor. – dopatraman May 07 '19 at 19:36
  • 15
    `IsAuthenticated` and `ScopedRateThrottle` are not instances, these are classes. They are instantiated when a FooView is constructed (actually, when the FooView processes a request). Anyway, this is merely an implementation detail. `IsAuthenticated` and `ScopedRateThrottle` are the dependencies; they are injected into the `FooView`. It doesn't matter _when_ or _how_ this is done. Python is not Java, so there are different ways to implement this. – Max Malysh May 07 '19 at 20:36
  • 19
    @MaxMalysh I agree with dopatraman on this one. This is not even IoC as the class itself has "hardcoded" dependencies to a specific class. In IoC, the dependency shoudl be provided instead of hardcoded. In top of that, in Dependency Injection, you will have an entity responsible of managing the lifecycles of each service and inject them when that is the case. The solution provided in not any of those. – Ricardo Alves Nov 26 '19 at 20:15
  • 3
    First of all, you are mixing IoC and DI. They are not the same thing. IoC is a generic concept which has nothing to do with language-specific semantics. You can have IoC in Lisp or in plain C. As for DI, what you are actually talking about is how DI **Frameworks** work in **Java and C#**. There are various ways to implement simple DI even in Java / C# (e.g. using a constructor, using a setter method, using interfaces). As for `FooView`... These dependencies _are not hardcoded_; you can swap or mock them in 1 line of code. Duck. Typing. – Max Malysh Nov 26 '19 at 21:20
  • 7
    @MaxMalysh, you shouldn't be touching 1 line of code in your class if you are passing in a mock or not... – PmanAce Feb 21 '20 at 18:52
  • 2
    @PmanAce yes, code won't change if you are passing a mock. You swap _dependencies_ or mock them _inside tests_. The original code stays the same. – Max Malysh Feb 27 '20 at 15:56
  • 3
    This isn't DI. What if you wanted to switch from JSONRenderer to XMLRenderer? You now have to change your code. I'm really curious how you'd mock IsAuthenticated, ScopedRateThrottle or those other classes in a unit test since you have no where to pass it in. – alex Apr 14 '20 at 12:04
  • 9
    @alex No, you don't need to change your code to use another renderer. You can even use multiple renderers simultaneously: `renderer_classes = (JSONRenderer, BrowsableAPIRenderer, XMLRenderer)`. Mocking is as simple as `@unittest.patch('myapp.views.FooView.permission_classes')`. A desperate need to "pass something" is a consequence of the "Java way of doing things" due to Java being a compiled and statically typed language lacking strong metaprogramming capabilities. – Max Malysh Apr 14 '20 at 19:13
  • 4
    @MaxMalysh I also disagree. If we stick to the common DI definition: "dependency injection is a technique whereby one object (or static method) supplies the dependencies of another object" then it's fine with `settings.py` but invalid for `FooView` view because the view it declaring it's own attributes (permission_classes, etc.) and not receiving them from the outside. – pgorecki May 28 '21 at 07:56
  • @pgorecki "..ecause the view it declaring it's own attributes (permission_classes, etc.) and not receiving them from the outside.." you mean it would be a factory pattern – user2290820 Nov 11 '21 at 00:21
  • 1
    @pgorecki Look outside the Django REST example. It's made that way for ease-of-use. If we just move those attributes to a constructor `APIViewCreator(get=None, post=None, ..., authentication_class=..., permission_classes=...)` then it's now passed by the user. – Keto Feb 17 '22 at 07:39
  • @Max Malysh Your example is really confusing. You're suggesting that you don't need frameworks to implement DI and immediately starting to provide code snippets with frameworks that implements the DI. – wow so much info Aug 19 '22 at 23:05
  • @wow so much info These are web frameworks, not DI frameworks. I've simply provided an easy-to-understand example for Python developers. You can easily implement DI without any framework at all, in pure Python. Please ping me if you are really interested in such example & I'll append my answer. – Max Malysh Aug 26 '22 at 22:20
  • 1
    Note that in Python classes are objects, so despite not putting the dependencies into the constructor you could make your own class and change the dependencies. – aoeu256 Jun 11 '23 at 12:56
68

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.
  • 108,298
  • 33
  • 122
  • 127
  • 2
    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 Mar 17 '10 at 13:45
  • 3
    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 Mar 17 '10 at 13:56
  • @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 Mar 18 '10 at 19:05
  • 26
    Oversimplifying, answer, in real life, you rarely need just "a singleton", you need to control scope (you might need a thread local singleton, or a session singleton, and so on), this makes me think that the kind of problems solved in Python are not the kind of real world problems actually solved in an enterprise setting – Luxspes Dec 23 '12 at 06:09
  • 5
    Actually DI is about being able to test and decouple dependencies of code. Also the import feature is similar to static imports in Java, which let me import a single instance of an object. – Richard Warburton Nov 28 '14 at 12:47
  • 3
    "You can get a sort of "singleton" for free, just by importing it from a module."can be easily done in Java by declaring a static instance field and setting it to a value. This isn't a sol – ggranum Jan 02 '18 at 16:40
  • Unlike a constructor, importing a module doesn't allow you to pass parameters into during import. Whereas a constructor can have parameters. – CMCDragonkai Jul 27 '18 at 06:54
37

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
  • 2,392
  • 18
  • 15
  • 5
    What you call code is actually the wiring part. That would be the XML of your ioc framework. It could actually be written simply as `import psycopg2 as database_interface`. Put that line in a `injections.py` et voilà. – spectras Oct 23 '15 at 19:56
  • 43
    Erm. What your doing there is pretty much textbook imperative Daniel. – Shayne Nov 09 '15 at 16:23
  • 1
    It's definitely imperative code, but it's kind of functional because it uses a callable as a value. – Jeremy May 19 '16 at 23:41
  • 6
    Isn't that just First Class Functions though? https://en.wikipedia.org/wiki/First-class_function Just because you have and use them doesn't make your code Functional. There are quite a few side effects happening here (such as changing `self.database_interface`), which screams imperative. – hjc1710 Jul 21 '16 at 19:16
34

It seems that people really don't get what Dependency injection and inversion of control mean anymore.

The practice of using inversion of control is to have classes or functions that depend on other classes or functions, but instead of creating the instances whithin the class or function code it is better to receive them as parameters, so loose coupling can be achieved. That has many benefits as more testability and to achieve the liskov substitution principle.

You see, by working with interfaces and injections, your code gets more maintainable, since you can change the behavior easily, because you won't have to rewrite a single line of code (maybe a line or two on the DI configuration) of your class to change its behavior, since the classes that implement the interface your class is waiting for can vary independently as long as they follow the interface. One of the best strategies to keep code decoupled and easy to maintain is to follow at least the single responsibility, substitution and dependency inversion principles.

What's a DI library good for if you can instantiate an object yourself inside a package and import it to inject it yourself? The chosen answer is right, since java has no procedural sections (code outside of classes), all that goes into boring configuration xml's, hence the need of a class to instantiate and inject dependencies on a lazy load fashion so you don't blow away your performance, while on python you just code the injections in the "procedural" (code outside classes) sections of your code.

Tobias Feil
  • 2,399
  • 3
  • 25
  • 41
jhonatan teixeira
  • 760
  • 1
  • 6
  • 8
  • 4
    you still miss that an IoC/DI wire the objects together automatically. It's not much being able to do it at runtime (Java can do that via reflection anyway), it's that the framework takes care of it and you don't need to do it explicitly. Having procedural sections is irrelevant either, nothing prevents one to write an entirely procedural app in Java, by using classes as mere containers of static subroutines and functions, without using OOP features at all. – zakmck Jan 11 '20 at 10:04
  • 3
    @zakmck: the "procedural" section of Python here isn't really about writing procedural code. What makes the "procedural" section of Python different than static languages is the ability to put procedural code in a class body, which runs during class definition time, and put import statements inside if-statement, and to create class factory simply by defining classes inside a factory method. These are things that you can't really do in static languages, and which solves most of the problems that IOC/DI tried to solve. Metaprogramming in Python often looks just like regular Python code. – Lie Ryan Jan 20 '20 at 07:22
  • 3
    @LieRyan, you can do that with reflection, or, if you need it often or at runtime, you can call the static language from another language like Groovy (which is designed to easily play with Java), or even Python itself. Yet, that has little to do with IoC/DI frameworks, for their purpose is to do most of the procedural object wiring for you, automatically, leveraging on definitions only. Sadly, most of the hereby answers miss this point. – zakmck Jan 20 '20 at 16:19
14

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
  • 2,345
  • 12
  • 12
  • 6
    It never ceases to amaze me that people willing to change how a system works to test that it worked. Now you need to test that your tests don't cause side-effects. – Basic Jul 26 '16 at 14:49
  • 3
    he talks about changing puts method only in tests scope, it is like mock method of injected object. – dpa Nov 24 '16 at 17:58
  • 3
    @Basic that's pretty normal in *unit tests*, actually it is advisable to do that in these tests as you don't want to pollute your test case coverage with more than one block of code (the one that is being tested). It would be wrong to do that for integration tests though, maybe that is what you are referring to on your comment? – samuelgrigolato Jan 14 '18 at 20:11
  • 4
    For me, testability is a first class concern. If a design isn't testable, it's not a good design, and I have no problem changing the design in order to make it more testable. I'll have to revalidate that it still works, but that's ok. Testability is a perfectly valid reason to change code IMO – Carlos Rodriguez Jan 13 '20 at 19:55
12

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(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!

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.

Community
  • 1
  • 1
mlvljr
  • 4,066
  • 8
  • 44
  • 61
  • 4
    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 Mar 18 '10 at 21:25
  • 1
    @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 Mar 18 '10 at 21:45
10

IoC/DI is a design concept, but unfortunately it's often taken as a concept that applies to certain languages (or typing systems). I'd love to see dependency injection containers become far more popular in Python. There's Spring, but that's a super-framework and seems to be a direct port of the Java concepts without much consideration for "The Python Way."

Given Annotations in Python 3, I decided to have a crack at a full featured, but simple, dependency injection container: https://github.com/zsims/dic . It's based on some concepts from a .NET dependency injection container (which IMO is fantastic if you're ever playing in that space), but mutated with Python concepts.

zsims
  • 101
  • 1
  • 3
8

I think due to the dynamic nature of python people don't often see the need for another dynamic framework. When a class inherits from the new-style 'object' you can create a new variable dynamically (https://wiki.python.org/moin/NewClassVsClassicClass).

i.e. In plain python:

#application.py
class Application(object):
    def __init__(self):
        pass

#main.py
Application.postgres_connection = PostgresConnection()

#other.py
postgres_connection = Application.postgres_connection
db_data = postgres_connection.fetchone()

However have a look at https://github.com/noodleflake/pyioc this might be what you are looking for.

i.e. In pyioc

from libs.service_locator import ServiceLocator

#main.py
ServiceLocator.register(PostgresConnection)

#other.py
postgres_connection = ServiceLocator.resolve(PostgresConnection)
db_data = postgres_connection.fetchone()
  • 2
    The very fact both version take the same amount of code goes a long way towards explaining why using a framework is not very popular. – spectras Oct 23 '15 at 20:22
  • In `other.py` line 1, there is an automated dependency resolution, but wouldn't count that as a dependency injection though. – andho Mar 20 '18 at 08:06
  • 1
    Service locators are usually an anti-pattern, just saying. – PmanAce Feb 21 '20 at 19:01
8

pytest fixtures all based on DI (source)

Martin Thoma
  • 124,992
  • 159
  • 614
  • 958
Meng Zhao
  • 131
  • 1
  • 6
7

Check out FastAPI, it has dependency injection built-in. For example:

from fastapi import Depends, FastAPI

async def get_db():
    db = DBSession()
    try:
        yield db
    except Exception:
        db.rollback()
        raise
    finally:
        db.close()

app = FastAPI()

@app.get("/items")
def get_items(db=Depends(get_db)):
    return db.get_items()
ospider
  • 9,334
  • 3
  • 46
  • 46
5

I back "Jörg W Mittag" answer: "The Python implementation of DI/IoC is so lightweight that it completely vanishes".

To back up this statement, take a look at the famous Martin Fowler's example ported from Java to Python: Python:Design_Patterns:Inversion_of_Control

As you can see from the above link, a "Container" in Python can be written in 8 lines of code:

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
Paolo Moretti
  • 54,162
  • 23
  • 101
  • 92
emilmont
  • 783
  • 8
  • 5
  • 55
    This falls far short of even the weakest DI containers. Where's the lifetime management, recursive dependency resolution, ability to mock, or - failing all that - configuration? This is nothing more than a type lookup and cache which is _not_ the same thing as IoC. – Basic Jul 26 '16 at 14:47
  • 2
    Years ago I wrote a [small DI framework](https://github.com/soulrebel/diy/blob/master/diy.py) using metaclasses as an exercise. The whole thing is a single file with zero imports and doctests that make it self-explainatory. It shows that basic features are not that hard to implement in a way that is even "pythonic", but I sincerely think it's sad that no complete solution has gotten major traction like Spring has in Java and everybody is doing custom plugin architectures. – Andrea Ratto Jun 07 '18 at 10:38
3

My 2cents is that in most Python applications you don't need it and, even if you needed it, chances are that many Java haters (and incompetent fiddlers who believe to be developers) consider it as something bad, just because it's popular in Java.

An IoC system is actually useful when you have complex networks of objects, where each object may be a dependency for several others and, in turn, be itself a dependant on other objects. In such a case you'll want to define all these objects once and have a mechanism to put them together automatically, based on as many implicit rules as possible. If you also have configuration to be defined in a simple way by the application user/administrator, that's an additional reason to desire an IoC system that can read its components from something like a simple XML file (which would be the configuration).

The typical Python application is much simpler, just a bunch of scripts, without such a complex architecture. Personally I'm aware of what an IoC actually is (contrary to those who wrote certain answers here) and I've never felt the need for it in my limited Python experience (also I don't use Spring everywhere, not when the advantages it gives don't justify its development overhead).

That said, there are Python situations where the IoC approach is actually useful and, in fact, I read here that Django uses it.

The same reasoning above could be applied to Aspect Oriented Programming in the Java world, with the difference that the number of cases where AOP is really worthwhile is even more limited.

zakmck
  • 2,715
  • 1
  • 37
  • 53
  • Is there a reference URL to source of information where django uses IoC? – Sajuuk Nov 20 '18 at 06:46
  • @Sajuuk, I've learned that about Django on this question's thread, so I don't know, you should ask the other answer authors. – zakmck Nov 20 '18 at 09:35
  • The first alinea of this answer adds 0 value in my opinion... I think I am capable of deciding when my python code would benefit from IoC, and I do not care about what developer thinks what is bad. I value pragmatism over unfundamented opinions. – Mike de Klerk Mar 01 '19 at 16:53
  • 1
    @MikedeKlerk my suggestion is that something that is both unknown (as many answers hereby prove) and victim of prejudge is unlikely to become popular, no matter how objective and well informed a few like you are. And of course I'm not sure this is a reason why you don't see many IoC usage in Python, I think the main reason is low/medium compexity apps don't need them. – zakmck Mar 04 '19 at 15:15
  • 1
    `The typical Python application is much simpler, just a bunch of scripts, without such a complex architecture.` - quite an assumption – hyankov Jan 06 '20 at 17:11
  • @hyankov "typical" doesn't mean "all of them". In fact, when that isn't the case, you might see IoCs being used (like in the mentioned Django). Moreover, even large Python codebases might be modular enough that they don't have complex networks of interdependent objects to manage. And no, it's not an assumption, but some experience with it. – zakmck Jan 06 '20 at 18:53
  • "An IoC system is actually useful when you have complex networks of objects, where each object may be a dependency for several others and, in turn, be itself a dependant on other objects. In such a case you'll want to define all these objects once and have a mechanism to put them together automatically, based on as many implicit rules as possible." In python, that is often solved simply with a factory function. – Lie Ryan Jan 20 '20 at 07:31
  • @LieRyan, which you have to write, so it doesn't replace a true IoC/DI framework, as I wrote above. Else, show me how you can declare a Service instance having setCredentials() or alike, then a Credentials instance and finally have them bound automatically, without any explicit wiring. – zakmck Jan 20 '20 at 16:26
  • @zakmck: You either have to write IOC/DI framework configuration, or you can write a factory. The Python code to do IOC/DI usually won't be any longer or more complex than the config file that you'd have to write in IOC/DI framework, due to the ease of use of dynamic features, ease of metaprogramming, duck typing, dynamic class definition, and the various nifty features of the language. In most cases, the Python code will be much simpler, shorter, and easier to reason about to do the same thing that an IOC/DI config does. So why use a framework when it doesn't actually reduce complexity? – Lie Ryan Jan 20 '20 at 16:42
  • @zakmck An IOC/DI configuration is essentially just a domain specific language (DSL) for describing wiring. Because metaprogramming is so simple in Python, Python is just its own DI/IOC DSL, and you don't really need a separate DSL with a separate syntax for it. – Lie Ryan Jan 20 '20 at 16:49
  • @LieRyan then show me how you can obtain that wiring without having to declare it explicitly. That is the main point of an IoC/DI framework and it becomes very useful when that kind of auto-wiring happens for complex networks of tens of classes. DSL, annotations (in Java or in Python), metaprogramming or whatever can't do any magic for you to avoid that kind of explicitness, you need a framework like Spring to have that done. Else, prove me wrong with the example above. – zakmck Jan 20 '20 at 17:15
  • @zakmck "without explicit wiring" is not the purpose of a DI/IOC framework, they don't actually solve that anyway. With IOC/DI framework **you still have to declare those wirings explicitly in XML**; it doesn't matter whether the wiring is defined in code or XML, it's just moving the code that's already short, simple, and clear in Python, to obtuse XML configuration that is hard to work with. With your example above is just wiring `Service(Credential(file))`, that's not even complex enough for even a static language to actually benefit from a DI framework. Explicit is better than implicit. – Lie Ryan Jan 20 '20 at 17:34
  • @LieRyan, no, you don't have to do `someService.setCredentials ( someCredentials )`, or its XML equivalent, you just need to declare the two objects and the framework will bind them together, by inferring the right wiring from the setter type and declared object types. You might have more complicated cases where you have to help the framework with some further declarations, but in general, things are designed to ease the network building. When you have tens of such bindings to set, explicit is a hell that you want to avoid, as anyone with some experience of that use case can tell you. – zakmck Jan 20 '20 at 18:12
  • By the way, Spring has supported Java annotations, Groovy and YAML as "obtuse XML" alternatives for years. – zakmck Jan 20 '20 at 18:19
  • XML, Annotation, Groovy, YAML, whatever, however you want to do it, you still have to encode the information somehow that you want things to connect or not connect, otherwise you might find yourself in a situation like the framework putting your APIService and RedisSession class together instead of APIService and TLSSession, because both session classes inherits from Serializable and the framework is too smart for itself. It doesn't make it shorter or easier to understand than if you just have a factory function instead when you just take advantage of Python's features. – Lie Ryan Jan 20 '20 at 22:58
  • @LieRyan Maybe you should study better what we're talking about. In most cases, there aren't such ambiguities and the IoC solves things for you. In a few cases, there might be >1 candidates for a dependency injection (TLSSession and RedisSession), the framework still provides flexible ways to resolve them (eg, inheritance+overriding of config files, object roles, name-based resolution). You can even resort to explicit wiring in a few points, yet most bindings are auto-resolved, even across multiple files provided at runtime. No plain Python can offer the same functionality. – zakmck Jan 20 '20 at 23:21
  • As a concrete example, see `@Autowired` and `@Resource` annotations in this package: tinyurl.com/sxe5kya You'll find single objects bound to multiple dependants. I just need to define the dependency and the binding fields in dependants. If I add/change dependants, even in separated package extensions, I just need to add fields and annotations, without any explicit binding instructions. I agree that you can emulate that in Python, as per examples in other answers, but that is like building a simple IoC/DI framework, it's not just using plain Python. I also agree you don't always need this. – zakmck Jan 20 '20 at 23:34
  • I've worked in really large code bases in either languages, I know enough about it to know that it doesn't really solve any actual problem that I ever had in Python. Injecting dependencies without a framework is simple enough in Python. It feels almost like telling people why Python don't need getters/setters, to people who just found an awesome magic framework to create getters/setters implicitly, solving issues that I don't really have to begin with. – Lie Ryan Jan 20 '20 at 23:50
  • You keep bringing up unrelated points, like this getters/setters thing (yes, property interceptors are great), and missing the main point of auto-wiring. Injecting dependencies in Python is simpler than in Java, agreed. Injecting dependencies **automatically**, using type inference or alike, is impossible in plain Python, as it is in almost any other language. So, you don't have such a necessity, not because of the language, but probably because of the typical use cases you use the language for, as I wrote initially in my answer. – zakmck Jan 21 '20 at 00:12
3

You can do dependency injection with Python manually, but manual approach has its downsides:

  • lots of boilerplate code to do the wiring. You can use dynamic features of Python to do the injection, but then you're loosing IDE support (e.g. Ctrl+Space in PyCharm), and you're making code harder to understand and debug
  • no standards: every programmer has its own way for solving same problems, this leads to reinventing the wheel, understanding each other's code can quickly become a pain. Dependency injection library provides easy framework to plug-in

To have it all we NEED a dependency injection framework, for example this one https://python-dependency-injector.ets-labs.org/index.html seems to be the most mature DI framework for Python.

For smaller apps DI container is not necessary, for anything that has few hundred lines of code or more, DI container is a must have to keep your code maintaineable.

iirekm
  • 8,890
  • 5
  • 36
  • 46
-1

I agree with @Jorg in the point that DI/IoC is possible, easier and even more beautiful in Python. What's missing is the frameworks supporting it, but there are a few exceptions. To point a couple of examples that come to my mind:

  • Django comments let you wire your own Comment class with your custom logic and forms. [More Info]

  • Django let you use a custom Profile object to attach to your User model. This is not completely IoC but is a good approach. Personally I'd like to replace the hole User model as the comments framework does. [More Info]

santiagobasulto
  • 11,320
  • 11
  • 64
  • 88
-2

IoC containers are "mimicked" mostly using **kwargs

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

Class B:
    pass

Class C:
    pass

Ainstance = A(b=B, c=C)
  • 2
    But what if `B` requires dependencies `I, J, K`, all of which also require one or more dependencies? Then you'd end up with `Ainstance = A(b=B(i=I(...), j=J(...), k=K(...))`. In contrast, you could have a framework look at type hints and/or some other form of config and instantiate all of those for you, and provide a fully instantiated `A` object for you. – Snake Verde Aug 12 '21 at 03:31
  • You are right! My bad. I did not think it that far. – Rambarun Komaljeet Aug 12 '21 at 08:22
-4

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
  • 18,290
  • 7
  • 46
  • 74
  • @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 Mar 17 '10 at 14:03
  • 4
    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 Mar 17 '10 at 14:08
  • 6
    @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 Mar 17 '10 at 15:17
  • 4
    @Finglas so if I have a dozen classes all using my `EmailSender` and decide to replace it with a `DesktopNotifier`, I have to go and edit 12 classes by hand. And you think that's simpler and cleaner that just writing to an `INotifier` interface and letting the container work out the details? – Basic Jul 26 '16 at 14:51
  • 2
    Unfortunately, a certain level of complexity is a reality that professional software developers must face. I see criticisms but no solutions in this answer. What is the "pythonic" solution for this problem: I am writing a library and I want to provide a hook for logging (something like PHP's PSR-3 LoggerInterface). I know how to use the log levels, but I don't care how the program actually reports them. What is the clean way to allow the client app to _inject_ that implementation detail. Note: other parts of the application may have different implementations of this interface. – Rob Jan 08 '17 at 14:55
  • @Rob: Complexity is inevitable, but DI/IoC frameworks are not. Python programs usually permit and encourage quite explicit and yet quite simple ways to configure apps based on data, which may differ from place to place, rather than employing a standard approach for the whole app. In your logging example, this is normally done via the standard library which lets you attach whatever log handlers you want, whenever you want. – Kylotan Jan 09 '17 at 10:20
  • If the log handlers are global, how can you configure implementations per component of your application? e.g. I want my authentication library to send `error` and above to sentry, but my HTML rendering library should just dump `error`s to `/var/log/html-errors.log` – Rob Jan 09 '17 at 18:17
  • @Rob - the system is globally available but the handlers don't have to be. All this is just part of how the standard logging system works. Docs are online. – Kylotan Jan 10 '17 at 10:41
  • 2
    My question for you is not how do you use the standard logging library, nor is it about creating different instances of a logger class. My question is how do you configure your application so that different parts of your application can use different implementations, and not be concerned with those details (provided that they know how to use the interface). This is a very real problem that DI has solved for multiple PHP applications I've worked on. I'm looking for the python equivalent. And suggesting "just don't make your application that complex" is not the answer I'm looking for. – Rob Jan 10 '17 at 19:18
  • @Rob: what you are looking for does not exist. Partly because the most common place where it's used (logging, as per your example) has its own solution, and other individual problems have their own answers. System-wide IoC/DI is rarely done in Python and it only seems to be people from other languages that miss it. – Kylotan Jan 13 '17 at 09:58
  • The need of adding interfaces you mention is just an implementation detail of IOC + DI in Java/C# and strongly typed languages like those. One can benefeit in Python of IoC/DI without interfaces, since in Python they are not needed to achieve said IOC. – miquelvir Jul 24 '21 at 21:46
-7

Unlike the strong typed nature in Java. Python's duck typing behavior makes it so easy to pass objects around.

Java developers are focusing on the constructing the class strcuture and relation between objects, while keeping things flexible. IoC is extremely important for achieving this.

Python developers are focusing on getting the work done. They just wire up classes when they need it. They don't even have to worry about the type of the class. As long as it can quack, it's a duck! This nature leaves no room for IoC.

Jason Ching
  • 1,991
  • 1
  • 19
  • 23