Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python difference between __import__ and import as

I am trying to dynamically import a configuration file in Python.

My code works fine when I use:

import conf.config as config

but doesn't work correctly when I use:

config = __import__("conf.config")

Below are to sample programs and the results I get when running them:

#regularimport.py
import conf.config as config

def read_values(cfg):
    for varname in cfg.__dict__.keys():
        if varname.startswith('__'):
            continue
        value = getattr(cfg, varname)
        yield (varname, value)

for name,value in read_values(config):
    print "Current config: %s = %s" % (name, value)

Results:

$python regularimport.py

Current config: SETTING_TWO = another setting
Current config: SETTING_ONE = some setting

Dynamic import:

#dynamicimport.py
conf_str = "conf.config"
config = __import__(conf_str)

def read_values(cfg):
    for varname in cfg.__dict__.keys():
        if varname.startswith('__'):
            continue
        value = getattr(cfg, varname)
        yield (varname, value)

for name,value in read_values(config):
    print "Current config: %s = %s" % (name, value)

Results:

$ python dynamicimport.py
Current config: config = <module 'conf.config' from '/home/ubuntu/importex/conf/config.pyc'>

My question is why the difference? And more importantly, how can I make the dynamic import example work as it does with the regular import?

like image 284
Steven Potter Avatar asked Jan 19 '14 04:01

Steven Potter


1 Answers

As the documentation explains:

When the name variable is of the form package.module, normally, the top-level package (the name up till the first dot) is returned, not the module named by name.

So, when you do this:

config = __import__("conf.config")

That's not the same as:

import conf.config as config

But rather something more like:

import conf.config
config = conf

Why?

Because conf, not conf.config, is the thing that gets bound by an import statement. (Sure, in import foo as bar, obviously bar gets bound… but __import__ isn't meant to be an equivalent of import foo as bar, but of import foo.) The docs explain further. But the upshot is that you probably shouldn't be using __import__ in the first place.


At the very top of the function documentation it says:

Note: This is an advanced function that is not needed in everyday Python programming, unlike importlib.import_module().

And at the bottom, after explaining how __import__ works with packages and why it works that way, it says:

If you simply want to import a module (potentially within a package) by name, use importlib.import_module().

So, as you might guess, the simple solution is to use importlib.import_module.


If you have to use Python 2.6, where importlib doesn't exist… well, there just is no easy solution. You can build something like import_module yourself out of imp. Or use __import__ and then dig through sys.modules. Or __import__ each piece and then getattr your way through the results. Or in various other hacky ways. And yes, that sucks—which is why 3.0 and 2.7 fixed it.

The 2.6 docs give an example of the second hack. Adapting it to your case:

__import__("conf.config")
config = sys.modules["conf.config"]
like image 114
abarnert Avatar answered Oct 20 '22 09:10

abarnert