34

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!

Darren McAffee
  • 645
  • 1
  • 6
  • 10
  • `from Animals.Mammals` will work if you are on the same directory level as the `Animals` module. And I don't think you need any imports inside of `__init__.py` unless you want to import additional modules when you do `import Animals` – OneCricketeer Jan 12 '16 at 20:36
  • Do you really mean you run the interpreter from inside `Project/` or do you mean you run it from inside `Animals`? If you have the file structure you describe and run the interpreter from `Project/` it should work with an empty `__init__.py` as well. – JoGr Jan 12 '16 at 20:43

1 Answers1

57

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.)

Demis
  • 5,278
  • 4
  • 23
  • 34
masnun
  • 11,635
  • 4
  • 39
  • 50
  • Thanks! This works. Do you know if this is a standard way to structure `__init__.py` files? – Darren McAffee Jan 12 '16 at 20:35
  • It is often done - mostly for commonly used classes, functions so they can be easily imported without typing long import paths. Please upvote and accept the answer if it helped you. – masnun Jan 12 '16 at 20:36
  • Can someone supply a reference on why there is a '.' before the imported file name? Looking at a package I constructed a few years ago, it appears that @DarrenMcAffee code should have worked as posed. – SeanM Mar 23 '17 at 17:57
  • It looks like this issue is a path directory problem. If the folder Project/Animals is properly added to the sys.path and/or PYTHONPATH then you don't need the .Mammals rather than Mammals. Clarification on this issue would be welcome. – SeanM Mar 23 '17 at 18:11
  • 17
    This is a non informative answer, as it 1. does not explain why the user code has such a problem 2. it does not explain why the solution fixes the problem. The only way to make use of this answer is by copy-pasting from it, and it does not help in understanding what happens. Please elaborate on the source of the problem, I'd love to upvote such answer – Makers_F Jul 19 '18 at 15:14
  • Added an attempt at explanation. – Demis Apr 25 '20 at 21:59
  • 2
    Reading through this article helped me to clearly understand the root cause of the issue: http://python-notes.curiousefficiency.org/en/latest/python_concepts/import_traps.html – Joarder Kamal Oct 01 '20 at 00:19
  • How would one go about importing one of these modules into another, e.g. for typing purposes, without causing an import loop error? e.g. you add a Penguin.py that extends Birds and import both to __init__ in this manner – ch4rl1e97 Aug 28 '23 at 23:14