__init__.py can't find local modules

Darren McAffee picture Darren McAffee · Jan 12, 2016 · Viewed 27k times · Source

Borrowing a simplified example at http://pythoncentral.io/how-to-create-a-python-package/

I have an analogous file structure as follows, where Mammals.py and Birds.py define classes with the same names:

Project/
  Animals/
    __init__.py
    Mammals.py
    Birds.py

When running an ipython interpreter within the Project/ directory and with __init__.py being empty, the following works:

from Animals.Mammals import Mammals
x = Mammals()
x.printMammals()

I'd like to be able to write from Animals import Mammals instead of from Animals.Mammals import Mammals. And I believe the way to do that is to make the __init__.py file contents the following:

from Mammals import Mammals
from Birds import Birds

However, when this is done, from within a similarly Project/ sourced ipython interpreter, the following input produces an error:

In [1]: from Animals import Mammals
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
<ipython-input-1-6d651848af9b> in <module>()
----> 1 from Animals import Mammals

/Users/username/Project/Animals/__init__.py in <module>()
----> 1 from Mammals import Mammals
      2 from Birds import Birds

ImportError: No module named 'Mammals'

I feel that there is simple mistake that I am making, but can't find. Thanks for any help!

Answer

masnun picture masnun · Jan 12, 2016

Put the following codes in the __init__.py inside the Animals directory.

Python 3.x :

from .Mammals import Mammals
from .Birds import Birds

On 2.x:

from __future__ import absolute_import
from .Mammals import Mammals
from .Birds import Birds

Explanation:

It can't find the module because it doesn't know what directory to search to find the files Mammals and Birds. You're assuming that the subfolder Animals gets added to the python search path, but if you check sys.path (executed from Projects/Animals/__init__.py) you'll see that only the path to Project is on the path. I'm not sure why the directory containing Project/Animals/__init__.py is not searched, since that's the code being executed, but the error indicates this is the cause.

Putting a . before the module name tells Python that the module you're loading is inside the current module's directory.

(Thanks to @SeanM's comment for explaining.)