I propose introducing a mechanism in Python that allows modules to accept initialization parameters upon import. This would enable passing instances or configurations directly to a module at the time of import, facilitating better modularity and dependency management.
I think both of these solutions are better than your proposal:
import module_a # look, I'm at the top
...
module_a.init(a) # less magic
module_a.use()
# or
class_a = module_a.ClassA(a) # no global configuration
class_a.use()
It requires a lot of work, and there’re really very few benefits, other than saving you from having to write a class, import it and initialise it (or use Nineteendo’s initialiser function).
That requires dynamic runtime behaviour to be used in pre-runtime type checking analysis then. Use a Generic class, or Generic function instead, with a type variable.
A well designed module should come with its own sensible default values, so it works out of the box.
Modules that initialize on import has been a source of enormous pain for me over the years.
I absolutely hate how Jax does this, for example. It means that I have carefully ensure that I call the Ray library’s initialization function before I even import Jax, which isn’t easy in a large program.
Especially since I don’t always want to initialize Ray.
Similarly, matplotlib has initialization code so if you want to update settings, you to set the rcParams at a special time.
In general, I think libraries that initialize on import are a mistake. These libraries should have instead used a context manager that initializes the library on enter and un-initializes it on exit.
I don’t think we should encourage libraries to have initialization code like in this proposal.
Are there existing mechanisms in Python that achieve similar functionality?
Have you tried putting your initialization code into a context manager, and using the returned context if necessary?
In a statement like import mod, the code text ‘mod’ is first used to search sys.modules, and if not found, to create a file path used to initiate a new module. In either case, it is then used as the target for an assignment. In import mod(a), ‘mod(arg)’ looks like a function call evaluated. But it is not: ‘mod’ is not an identifier mapped to any object, let alone a callable. But ‘arg’ must be a valid python expression and it must be evaluated in the local namespace. Is such fake function call syntax used elsewhere?
The proposal pushed further the idea that modules are (like) classes. Both are namespaces, but the differences are important. In particular, classes are usually instanced, and mutating one instance normally does not affect other instances. But mutating (monkey-patching) modules affects all users. Mixing it in with module creation makes it less visible. (See Neil’s comment about resulting pain.)
If two modules have import mod(a) and import mod(b), is init called only for the first import, when mod is created, or for each import? Really better to be explicit, or create separate class instances as Nice Zombies suggested.
What specific problems is the proposal trying to address? Can you provide real-life examples where these issues still persist and are not yet fully resolved?