Each Answer to this Q is separated by one/two green lines.
In the below hierachy, is there a convenient and universal way to reference to the top_package using a generic term in all .py file below? I would like to have a consistent way to import other modules, so that even when the “top_package” changes name nothing breaks.
I am not in favour of using the relative import like “..level_one_a” as relative path will be different to each python file below. I am looking for a way that:
- Each python file can have the same import statement for the same module in the package.
A decoupling reference to “top_package” in any .py file inside the package, so whatever name “top_package” changes to, nothing breaks.
top_package/ __init__.py level_one_a/ __init__.py my_lib.py level_two/ __init__.py hello_world.py level_one_b/ __init__.py my_lib.py main.py
This should do the job:
top_package = __import__(__name__.split('.'))
The trick here is that for every module the
__name__ variable contains the full path to the module separated by dots such as, for example,
top_package.level_one_a.my_lib. Hence, if you want to get the top package name, you just need to get the first component of the path and import it using
Despite the variable name used to access the package is still called
top_package, you can rename the package and if will still work.
Put your package and the
main script into an outer container directory, like this:
container/ main.py top_package/ __init__.py level_one_a/ __init__.py my_lib.py level_two/ __init__.py hello_world.py level_one_b/ __init__.py my_lib.py
main.py is run, its parent directory (
container) will be automatically added to the start of
sys.path. And since
top_package is now in the same directory, it can be imported from anywhere within the package tree.
hello_world.py could import
level_one_b/my_lib.py like this:
from top_package.level_one_b import my_lib
No matter what the name of the container directory is, or where it is located, the imports will always work with this arrangement.
But note that, in your original example,
top_package it could easily function as the container directory itself. All you would have to do is remove
top_package/__init__.py, and you would be left with efectively the same arrangement.
The previous import statement would then change to:
from level_one_b import my_lib
and you would be free to rename
top_package however you wished.
For example, suppose you wish to import
<whatever>.level_one_a.level_two.hello_world from somewhere else in the package. You could do something like this:
import os _temp = __import__(__path__.split(os.sep) + ".level_one_a.level_two.hello_world") my_hello_world = _temp.level_one_a.level_two.hello_world
This code is independent of the name of the top level package and can be used anywhere in the package. It’s also pretty ugly.
This works from within a library module:
import __main__ as main_package TOP_PACKAGE = main_package.__package__.split('.')
I believe #2 is impossible without using relative imports or the named package. You have to specify what module to import either by explicitly calling its name or using a relative import. otherwise how would the interpreter know what you want?
If you make your application launcher one level above
top_level/ and have it
import top_level you can then reference
top_level.* from anywhere inside the top_level package.
(I can show you an example from software I’m working on: http://github.com/toddself/beerlog/)