Importing modules with arguments

Chris Angelico rosuav at gmail.com
Fri Jul 30 14:05:24 EDT 2021


On Sat, Jul 31, 2021 at 3:48 AM Charles Smith <charles at sollum.tech> wrote:
>
> I have found myself wanting to import module and provide arguments to them. There's two main reason I could think of for this. First is to prevent a circular import, though most of circular imports can be prevented by changing the design. The second reason is to invert dependencies between two modules. It occurred to me when using libraries like FastAPI or Flask that it would be nicer to use and leaner if instead of:
> 1. Import FastAPI Router or FastAPI object directly
> 2. Create a Router
> 3. Use the route decorators
>
> We could simply import a module with importlib and provide the router itself. You can then keep all the FastAPI related code inside its module and only have clean list of endpoints. Now this is actually doable right now, here's the snippet: https://gist.github.com/BinarSkugga/c281cbbe36e7f11bc0fd143ea1bb4dd4
>
> Disclaimer: I have no idea what are the implication of this but I'd like to have it as a feature instead of this hack.
>

One problem here is that imports are cached. If module #1 says "import
flask", Python runs the module and gives back the result; if module #2
subsequently says "import flask", Python returns the exact same module
from the cache. That won't work well with parameterization.

I would recommend either:

1) Import a class from the module, then instantiate; or
2) Import the module, then make a change to its globals.

The first one allows different modules to have different parameters;
the second would have different parameters overwrite each other. (And
it would be very clear that it's going to do so.)

The idiom "from flask import Flask; flask = Flask()" is a bit clunky,
but it's probably the best you have for adding parameters. (In this
specific case, "app = Flask()", which is more common, but same
difference.) I can't really imagine shortening it very much; a syntax
like "import flask()" would be confusing, and every other syntax I can
think of will be only marginally better than the standard idiom.

There is one important situation where you can't just import the whole
module and then do stuff, and that's choosing to import *less* than
the full module. For that use-case, it kinda has to be a package, but
then you should be able to do something like:

package/__init__.py
# all the basic stuff in this file

package/expensive1.py
package/expensive2.py
package/expensive3.py
# different parts

package/all.py
from . import expensive1, expensive2, expensive3

Then you can say something like:

import package.expensive2

and it'll only get some part of the package. It's a bit unideal in
that the namespace has to reflect this, but with careful use of
__getattr__, you could conceal those details (at the cost of even more
complexity). On the upside, though, the parameterization doesn't have
to break anything - if module 1 imports one part of the package, and
module 2 imports another part, both parts will coexist nicely.

ChrisA


More information about the Python-list mailing list