I have a directory structure as follows:
test/
__init__.py
m1/
__init__.py
f1.py
f2.py
test/__init__.py
is empty.
test/m1/__init__.py
contains a single line import test.m1.f1
.
test/m1/f1.py
contains a single line import test.m1.f2 as f2
.
In python 3.7.6, I can do import test.m1
and everything works as expected. However, in python 3.6.9 when I try this I get the following error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/scratch/test/m1/__init__.py", line 2, in <module>
import test.m1.f1
File "/home/scratch/test/m1/f1.py", line 1, in <module>
import test.m1.f2 as f2
AttributeError: module 'test' has no attribute 'm1'
This seems strange, because it does not error on the import test.m1.f1
, which is the first thing it encounters. It errors on a subsequent import test.m1.f2 as f2
statement, claiming that test
has no m1
submodule.
This is caused by the fact that the version of Python you're running your script with is not configured to search for modules where you've installed them. This happens when you use the wrong installation of pip to install packages.
Import order does not matter. If a module relies on other modules, it needs to import them itself. Python treats each . py file as a self-contained unit as far as what's visible in that file.
Python's ImportError ( ModuleNotFoundError ) indicates that you tried to import a module that Python doesn't find. It can usually be eliminated by adding a file named __init__.py to the directory and then adding this directory to $PYTHONPATH .
Since there are no finders, Python can’t find or import new modules. However, Python can still import modules that are already in the module cache since it looks there before calling any finders. In the example above, importlib was already loaded under the hood before you cleared the list of finders.
The import system — Python 3.9.0 documentation. 5. The import system ¶. Python code in one module gains access to the code in another module by the process of importing it. The import statement is the most common way of invoking the import machinery, but it is not the only way.
A cyclical import happens when you have two or more modules importing each other. More concretely, imagine that the module yin uses import yang and the module yang similarly imports yin. Python’s import system is to some extent designed to handle import cycles.
The import statement is the most common way of invoking the import machinery, but it is not the only way. Functions such as importlib.import_module () and built-in __import__ () can also be used to invoke the import machinery. The import statement combines two operations; it searches for the named module,...
import test.m1.f2 as f2
tries to access the m1
attribute of the test
module object, as part of the process of finding the object to bind to f2
. The m1
attribute won't be set until the test.m1
subpackage finishes initializing, which won't happen until the __init__.py
for test.m1
finishes executing.
On Python 3.7 and up, if the attribute lookup fails, the import falls back to a sys.modules['test.m1.f2']
lookup to find test.m1.f2
. This fallback does not exist on 3.6, causing the observed discrepancy.
A similar fallback also exists for circular from
imports on Python 3.5 and up, though from . import f2
or from test.m1 import f2
wouldn't need the fallback. The from
import retrieves test.m1
straight from sys.modules
, so it only looks for the f2
attribute, and that attribute is present.
This is not the answer explaining the how, but it explains how you can avoid the error. So useful for the ones wanting to write code that runs under 3.6 or earlier.
Replace
import test1.m1.f2 as f2
with
from test.m1 import f2
Or as @alaniwi pointed out
from . import f2
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