-2

I am working on a group project of mine and every part of the code is quite messy. I want to improve readability of the imports. In my opinion 20 lines of messy imports hurts the main structure of the code.

So here is a part of the imports:

import datetime
import os
import json
import re
import psycopg2 as dbapi2
from datetime import datetime
from datetime import date
from flask import Flask, jsonify
from flask import redirect

And I want to import all of these with something like:

import importLibs
importLibs()

And for this I tried the solution from How to make global imports from a function? and came up with this:

def importLibs():
    globals()['datetime'  ]       = __import__('datetime'         )
    globals()['os'        ]       = __import__('os'               )
    globals()['json'      ]       = __import__('json'             )
    globals()['re'        ]       = __import__('re'               )
    globals()['dbapi2'    ]       = __import__('psycopg2'         )
    globals()['date'      ]       = __import__('datetime'         )
    globals()['Flask'     ]       = __import__('flask.Flask'      )
    globals()['jsonify'   ]       = __import__('flask.jsonify'    )
    globals()['redirect'  ]       = __import__('flask.redirect'   )

But it fails with:

ModuleNotFoundError: No module named 'flask.Flask'
martineau
  • 119,623
  • 25
  • 170
  • 301
  • 5
    "In my opinion 20 lines of messy imports hurts the main structure of the code." It really doesn't. –  Nov 23 '18 at 22:23
  • 2
    To echo the above comment; your solution to a listing of imports (which many IDEs will collapse for you) is to add a layer of indirection. Even if this works, when you need to reference a dependency, you will have to first go to a completely different file. Trust us when we say this ultimately will make your life harder. – Nathaniel Ford Nov 23 '18 at 22:27
  • If you want to improve readability, isn't it better to use a style guide made by people with deep understand of how Python works, with guidelines that are actually used by the community? Bear in mind that readability comes also from following known and established patterns. I recommend you check out [PEP 8](https://www.python.org/dev/peps/pep-0008/#imports) instead of going into trouble to implement things that might do the exact opposite of what you're intending to do. – Aurora Wang Nov 23 '18 at 22:38
  • Actually, you are all right. It was silly for me to think this would be better coding convention. IDEs would collapse the importing segments anyway. Thanks for your attention. – Talip Tolga Sarı Nov 23 '18 at 22:58

3 Answers3

2

Warning: What you're doing is almost certainly a bad idea.


The reason for the error is that flask.Flask is a class, not a module.

If you really have to do this, you might try:

__import__('flask').Flask
__import__('flask').jsonify
__import__('flask').redirect

However, this seriously hurts your code's readability.

By having the imports laid out at the top of your module, you can immediately see what your code is using. By moving it into a separate file, you're hiding your code's dependencies and making it much harder to understand what's going on.

Putting imports at the top of a file is standard practice in every language I can think of. Consider what somebody is going to think when they see this. Why have they done it this way? That looks really complicated. Is there something I'm missing? Why didn't they just use import statements like everyone else?

By the time they've figured out what you're doing, they've wasted 5 minutes of their time. It's a distraction.

Finally, remember that the more complicated you make your code, the more space there is for bugs to creep in!

Will Vousden
  • 32,488
  • 9
  • 84
  • 95
  • 2
    Well, you have a great point there. And I could just collapse the imports within the IDE with no problems actually. Less lines of code does not necessarily mean readable code. Thanks. – Talip Tolga Sarı Nov 23 '18 at 22:56
0

Don't solve a problem that doesn't exist.

Every Python programmer already knows exactly what to expect with imports. That hacky importLibs() function just adds an extra level of indirection and needless complexity.

You should follow the standard guidance for imports given in PEP8: https://www.python.org/dev/peps/pep-0008/#imports

For the specific imports in question, you might want to alphabetize and separate external modules from standard library modules. Besides that, just leave them alone.

The following is a slight cleanup that looks perfectly readable (and standard):

import datetime
import json
import os
import re
from datetime import date

import psycopg2 as dbapi2
from flask import Flask, jsonify, redirect
Corey Goldberg
  • 59,062
  • 28
  • 129
  • 143
  • 1
    Note that (like the OP's original code) you're shadowing the `datetime` *module* with the `datetime.datetime` *class*. – jonrsharpe Nov 24 '18 at 09:15
0

First, the idiomatic Python ("pythonic") style is to use the 20 lines of explicit import statements. Static analysis tools tend to function better if you don't try to circumvent the normal import mechanisms. Some IDEs like PyCharm can keep them sorted for you.

Dynamic imports like that are much harder to understand without actually running the code.

That said, some libraries do use runtime imports like this. Sometimes what you need to import depends on runtime conditions, so and it's worth learning how dynamic imports work.

Second, the __import__ builtin is not generally recommended because it doesn't handle the . for you when modules are nested. Use importlib.import_module instead. But in the case of Flask, it's not a module, but a class, so use import_module('flask').Flask.

Third, modules are not callable. You can't simply import something and call it, like you suggest.

import importLibs
importLibs()

instead it should be something like

from my_imports import importLibs

It is possible to place arbitrary objects into the import cache by mutating sys.modules, so you could make a direct import callable that way. Modules have been known to replace themselves with other things on import like that. Please use responsibly, as this is again harder to reason about without actually running the code.

Fourth, the globals() function refers to the globals of the defining module. Not the module it was called in. So you can't just from my_imports import importLibs and then expect it to import things into the current module. They'll go inside the my_imports module instead. There are at least two ways to deal with this.

One is to add some kind of globals_dict parameter to the function and set the globals on that instead of globals(). Then you'd call it like importLibs(globals()). This way it will use the globals dict from the calling module instead of the defining (my_imports) module.

The other way is to use the inspect module to go up a stack frame and find the caller's module to get its globals. This way you won't have to pass it in explicitly and can just use importLibs(). But this kind of automagic is more brittle and can break if you somehow call it indirectly inside a different module.


Another option is to import everything into your third module my_imports, provide an __all__ there and then use

from my_imports import *

And if you manage your global namespace carefully, you don't even need the __all__. Star imports are discouraged, because explicit imports are easier to reason about statically, but some libraries do work this way.


And finally, you could make a module with a short name, import everything there and then just access things via . from that everywhere, like

import my

my.Flask

Again, some libraries work this way, like tk.

gilch
  • 10,813
  • 1
  • 23
  • 28
  • What I had in mind was to have some kind of "requirements.txt" for the python imports. It adds a layer of distraction, but it makes code tidy in my opinion. If you have 10's of imports I thought it was better to organize them like this. I'll look into pep8 guide like everyone suggested. Thank you for effort :) – Talip Tolga Sarı Nov 24 '18 at 08:56