Dependency injection in imported python module
Question: My python module uses some functions from another module, but I have several
implementations of that module interface. How to point out, which one to use?

Simple example:

A.py:

    
    
    import B
    def say_hi()
       print "Message: " + B.greeting()
    

main.py:

    
    
    import A(B=my_B_impl)
    A.say_hi()
    

my_B_impl.py:

    
    
    def greeting():
       return "Hallo!"
    

output:

    
    
    Message: Hallo!
    


Answer: What you ask is not directly possible. There is no parameterisation capability
built in to Python's module system. If you think about it, it's not clear how
such a proposal ought to work: if modules `A` and `B` both import module `M`,
but they supply different parameters, which parameter is used when `M` is
imported? Is it imported twice? What would that mean for module-level
configuration (as in `logging`)? It gets worse if a third module `C` attempts
to import `M` without parameters. Also, the "open-world" idea that you could
override _any_ `import` statement from the outside violates the language-
design principle that "the code you wrote is the code that ran".

Other languages have incorporated parameterised modules in a variety of ways
(compare Scala's object model, ML's modules and signatures, and - stretching
it - C++'s templates), but it's not clear that such a feature would be a good
fit for Python. (That said, you could probably hack something resembling
parameterised modules using `importlib` if you were determined and masochistic
enough.)

* * *

Python does have very powerful and flexible capabilities for dynamic dispatch,
however. Python's standard, day-to-day features like functions, classes,
parameters and overriding provide the basis for this support.

There are lots of ways to cut the cake on your example of a function whose
behaviour is configurable by its client.

A function parameterised by a value:

    
    
    def say_hi(greeting):
        print("Message: " + greeting)
    
    def main():
        say_hi("Hello")
    

A class parameterised by a value:

    
    
    class Greeter:
        def __init__(self, greeting):
            self.greeting = greeting
        def say_hi(self):
            print("Message: " + self.greeting)
    
    
    def main():
        Greeter("Hello").say_hi()
    

A class with a virtual method:

    
    
    class Greeter:
        def say_hi(self):
            print("Message: " + self.msg())
    
    class MyGreeter(Greeter):
        def msg(self):
            return "Hello"
    

A function parameterised by a function:

    
    
    def say_hi(greeting):
        print("Message: " + greeting())
    
    def make_greeting():
        return "Hello"
    
    def main():
        say_hi(make_greeting)
    

There are more options (I'm avoiding the Java-y example of objects invoking
other objects) but you get the idea. In each of these cases, the selection of
the behaviour (the passing of the parameter, the overriding of the method) is
decoupled from the code which uses it and could be put in a different file.
The right one to choose depends on your situation (though here's a hint: the
right one is always the simplest one that works).

* * *

**Update** : in a comment you mention that you'd like an API which sets up the
dependency at the module-level. The main problem with this is that the
dependency would be _global_ \- modules are singletons, so anyone who imports
the module has to use the same implementation of the dependency.

My advice is to provide an object-oriented API with "proper" (per-instance)
dependency injection, and provide top-level convenience functions which use a
(configurable) "default" set-up of the dependency. Then you have the option of
_not_ using the globally-configured version. This is roughly [how `asyncio`
does it](https://docs.python.org/3/library/asyncio-eventloops.html#event-loop-
functions).

    
    
    # flexible object with dependency injection
    class Greeter:
        def __init__(self, msg):
            self.msg = msg
        def say_hi(self):
            print("Message: " + self.msg)
    
    # set up a default configuration of the object to be used by the high-level API
    _default_greeter = Greeter("Hello")
    def configure(msg):
        global _default_greeter
        _default_greeter = Greeter(msg)
    
    # delegate to whatever default has been configured
    def say_hi():
        _default_greeter.say_hi()
    

