I read the documentation for __init__.py files and some interesting questions here on SO, but I'm still confused about the correct use.
Context
I have code with many packages and subpackages. I have defined many classes, some of which I need to create one (and only one) instance for the entire user session. These new objects are then used in different parts of the code, so that every time I (or the user) update the data / information in these objects, it will be used in the entire code without changing anything else. To be more clear, let me show you the basic outline of what I'm talking about.
The code has a simplified structure, for example:
root/ __init__.py tools/ __init__.py ... (some modules) aliases.py (which defines the class Aliases) physics/ __init__.py ... (some modules) constants/ ... (some modules) constants.py (which defines the class Ctes) units.py (which defines the class Units)
In the code, I need to manage aliases, units and constants. The way I found this is to create one instance of each and use it throughout the code. Using this method, I am sure, for example, that if an alias is added while the code is still running, it can be used anywhere in the code, because there is only one common instance of the aliases. This is what I need (the same thing applies to units and to constants).
Current state
So far, how I do it, I think, is not the best. In fact, I instantiate, say, aliases, right after the class declaration in the same file:
in root/tools/aliases.py
import ... (stuff class Aliases(object): """The Aliases manager""" ... ALIASES = Aliases()
And then, in any file I need to use Aliase, I do:
in any_file.py (anywhere in the code)
from root.tools.aliases import ALIASES ALIASES.method1() # possibly in functions or other classes ALIASES.method2() # ... etc
And for some other classes, I even use the __init__.py file in the root of the code:
in root/__init__.py
# CTES is the instance of Ctes() created in root/physics/constants/constants.py from root.physics.constants.constants import CTES CTES.add(...)
(of course, CTES does not just save some constants, I define some methods for using them, so it makes sense to have them in this class, and not just define them like regular python constants in a module)
Questions
I am wondering if I am doing this correctly (maybe not). It might be better to use __init__.py files and initiate shared instances in it. But then it causes some problems (e.g. dependency cycles or increased memory usage ...)? Also, how do you use instantiated instances elsewhere in the code? Like this?:
in root/tools/__init__.py
import root.tools.aliases as Al ALIASES = Al.Aliases()
and then in any_file.py
from root.tools import ALIASES ALIASES.method(...)
Or should all of these instances be better included in a file (e.g. root/shared.py ) that I import into root/__init__.py so that I am sure that it is triggered?
I have read many times that it is better to leave __init__.py files empty (which is the case right now in my code, except, of course, for root/__init__.py ). What do you think?
I lost a little (you probably could see this because I am not very clear). Any help / advice is more than welcome. I would like to avoid any non-pythonic solutions, or solutions that can confuse the user, or make things unsafe.