My tree looks like
parent/
|--__init__.py
\--a.py
And the content of __init__.py
is
import parent.a as _a
a = 'some string'
When I open up a Python at the top level and import parent.a
, I would get the string instead of module. For example import parent.a as the_a; type(the_a) == str
.
So I think OK probably import
is importing the name from the parent
namespace, and it's now overridden. So I figure I can go import parent._a as a_module
. But this doesn't work as there is "No module named _a".
This is very confusing. A function can override a module with the same name, but a module cannot take on a new name and "reexport".
Is there any explanation I'm not aware of? Or is this documented feature?
Even more confusing, if I remove the import
statement in __init__.py
, everything is back normal again (import parent.a; type(parent.a) is module
). But why is this different? The a
name in parent
namespace is still a string.
(I ran on Python 3.5.3 and 2.7.13 with the same results)
In an import
statement, the module reference never uses attribute lookups. The statements
import parent.a # as ...
and
from parent.a import ... # as ...
will always look for parent.a
in the sys.modules
namespace before trying to further initiate module loading from disk.
However, for from ... import name
statements, Python does look at attributes of the resolved module to find name
, before looking for submodules.
Module globals and the attributes on a module object are the same thing. On import, Python adds submodules as attributes (so globals) to the parent module, but you are free to overwrite those attributes, as you did in your code. However, when you then use an import with the parent.a
module path, attributes do not come into play.
From the Submodules section of the Python import system reference documentation:
When a submodule is loaded using any mechanism [...] a binding is placed in the parent module’s namespace to the submodule object. For example, if package
spam
has a submodulefoo
, after importingspam.foo
,spam
will have an attributefoo
which is bound to the submodule.
Your import parent.a as _a
statement adds two names to the parent
namespace; first a
is added pointing to the parent.a
submodule, and then _a
is also set, pointing to the same object.
Your next line replaces the name a
with a binding to the 'some string'
object.
The Searching section of the same details how Python goes about finding a module when you import:
To begin the search, Python needs the fully qualified name of the module [...] being imported.
[...]
This name will be used in various phases of the import search, and it may be the dotted path to a submodule, e.g.
foo.bar.baz
. In this case, Python first tries to importfoo
, thenfoo.bar
, and finallyfoo.bar.baz
. If any of the intermediate imports fail, aModuleNotFoundError
is raised.
then further on
The first place checked during import search is
sys.modules
. This mapping serves as a cache of all modules that have been previously imported, including the intermediate paths. So iffoo.bar.baz
was previously imported,sys.modules
will contain entries forfoo
,foo.bar
, andfoo.bar.baz
. Each key will have as its value the corresponding module object.During import, the module name is looked up in
sys.modules
and if present, the associated value is the module satisfying the import, and the process completes. [...] If the module name is missing, Python will continue searching for the module.
So when trying to import parent.a
all that matters is that sys.modules['parent.a']
exists. sys.modules['parent'].a
is not consulted.
Only from module import ...
would ever look at attributes. From the import
statement documentation:
The
from
form uses a slightly more complex process:
- find the module specified in the from clause, loading and initializing it if necessary;
- for each of the identifiers specified in the import clauses:
- check if the imported module has an attribute by that name
- if not, attempt to import a submodule with that name and then check the imported module again for that attribute
- [...]
So from parent import _a
would work, as would from parent import a
, and you'd get the parent.a
submodule and the 'some string'
object, respectively.
Note that sys.modules
is writable, if you must have import parent._a
work, you can always just alter sys.modules
directly:
sys.modules['parent._a'] = sys.modules['parent.a'] # make parent._a an alias for parent.a
import parent._a # works now
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With