awforsythe
awforsythe

Reputation: 191

How do I make classes from a submodule available in a parent module's namespace?

Here's a python module. foo is in sys.path.

foo\
    __init__.py
    bar\
        __init__.py
        base.py
            class Base(object)
        derived.py
            import foo.bar.base as base
            class Derived(base.Base)

I've got nothing fancy going on yet. If I want to instantiate the Derived class from the derived module, I can do that easily enough:

import foo.bar.derived as derived
print(derived.Derived())

However, I'd like to just import the bar module and call bar.Derived(), because I plan to have lots of classes within lots of different modules, and I don't want to deal with all these tentacular import paths. My understanding is that I can simply import Derived into the namespace of the bar module, by modifying my project like so:

foo\
    __init__.py
    bar\
        __init__.py
            from foo.bar.derived import Derived
        base.py
            class Base(object)
        derived.py
            import foo.bar.base as base
            class Derived(base.Base)

Now I should be able to do the following:

import foo.bar as bar
print(bar.Derived())

But I get an AttributeError complaining that the foo module has no submodule called bar:

test.py             (1): import foo.bar
foo\bar\__init__.py (1): from foo.bar.derived import Derived
foo\bar\derived.py  (1): import foo.bar.base as base

AttributeError: 'module' object has no attribute 'bar'

In fact, my original test code (at top) doesn't work either! As soon as I try to import foo.bar, I get errors.

What I can gleam from this error is that the import statement in __init__.py causes derived.py to be executed before bar is fully loaded, and therefore it can't import the module (also from bar) which contains its own base class. I'm coming from the C++ world, where ultra-nested namespaces aren't as integral and a simple forward declaration would negate this problem, but I've been led to believe that what I'm looking for is possible and at least a somewhat acceptably Pythonic solution. What am I doing wrong? What's the correct way to make classes from a submodule available in the parent module's namespace?

Upvotes: 2

Views: 4671

Answers (2)

Gerrat
Gerrat

Reputation: 29680

in derived.py, use this:

EDIT: as JAB pointed out, implicit relative imports are deprecated, to the following isn't recommended (although it does work still in Python 2.7 - with no deprecation errors!).

import base # this is all you need - it's in the current directory

Instead, use:

from . import base # 

(or)

from foo.bar import base

instead of:

import foo.bar.base as base

This will solve both your errors (since they're from the same issue). Your import doesn't work since there is no base function or class inside the foo.bar.base module.

Upvotes: 1

JAB
JAB

Reputation: 21079

If you're working with Python 2.5 or later, try using explicit relative imports (http://www.python.org/dev/peps/pep-0328/#guido-s-decision):

test.py             (1): import foo.bar
foo\bar\__init__.py (1): from .derived import Derived
foo\bar\derived.py  (1): from . import base

(Note that if you are indeed working with Python 2.5 or 2.6, you'll need to include from __future__ import absolute_import in your modules.)

Upvotes: 5

Related Questions