Adding a bit of sophistication to the existing answers....
Depending on the use case, it may be somewhat inconvenient to have to explicitly specify the full path (E.g. package.subpackage.module...
) of the class/method you want to import. On top of importlib, we can leverage __init__.py
to make things even cleaner.
Let's say I have a python package, like so:
├── modes
│ ├── __init__.py
│ ├── bar.py
│ ├── foo.py
│ ├── modes.py
foo.py
, say, have some class/functions we'd like to use somewhere else in our program:
from modes.modes import Mode
class Foo(Mode):
def __init__(self, *arg, **kwargs):
super(Foo, self).__init__(*arg, **kwargs)
def run(self):
self.LOG.info(f"This is FOO!")
With a command line argument, I can pass an argument that corresponds to a mode that I want to run. I'd like to be able to so something like this:
def set_mode(mode):
""" """
import importlib
module = importlib.import_module('modes.foo')
getattr(module, mode)().run()
which outputs:
>> set_mode("Foo")
>> engine_logger:INFO - This is FOO!
That works fine, however what we'd REALLY want to get at is this:
def set_mode(mode):
""" """
import importlib
module = importlib.import_module('modes') # only import the package, not modules explicitely
getattr(module, mode)().run()
Which raises an error:
>> set_mode("Foo")
>> AttributeError: module 'modes' has no attribute 'Foo'
However, we can add the following to /modes/__init__.py
:
from .foo import Foo
from .bar import Bar
Then, we can do:
>> set_mode("Foo")
>> engine_logger:INFO - This is FOO!
>> set_mode("Bar")
>> engine_logger:INFO - This is BAR!
In other worlds, all sub modules/functions/classes we import in init.py will be found directly with importlib.import_module(...), without having to specify the full path from outside.