2

Imagine I have a module with two files, like this:

mymodule
 |-- __init__.py
 `-- submodule.py

mymodule/__init__.py contains:

SOME_CONSTANT_ONE = 1
SOME_CONSTANT_TWO = 2
SOME_CONSTANT_THREE = 3
...
SOME_CONSTANT_ONE_HUNDRED = 100

def initialize():
    pass # do some stuff

def support_function():
    pass # something that lots of other functions might need

I already know that I can use a relative import to bring in specific objects from the __init__.py file, like this:

submodule.py:

from . import initialize, support_function

def do_work():
    initialize() # initialize the module
    print(support_function()) # do something with the support function

But now what I want to know is if I can import all of the constants from the __init__.py file, but simultaneously have them appear in a namespace.

What won't work (what I've tried/considered):

  • import mymodule as outer_module works, since the import system already has knowledge of where the module is. However, if I ever need to change the name of the outer module, that code will break.
  • Doing import . as outer_module doesn't work.
  • Doing from . import * does work but puts all of the objects in __init__.py in the current namespace rather than in the sub-namespace.
  • Doing from . import SOME_CONSTANT_ONE as outer_constant_1, SOME_CONSTANT_TWO as outer_constant_2, SOME_CONSTANT_THREE as outer_constant_3, ... is ugly and won't bring in any new constants should they be defined later on in __init__.py.

What I really want is something like this:

submodule.py:

SOME_CONSTANT_ONE = "one!" # We don't want to clobber this.

import . as outer_module # this does not work, but it illustrates what is desired.

def do_work():
    print(SOME_CONSTANT_ONE)              # should print "one!"
    print(outer_module.SOME_CONSTANT_ONE) # should print "1"

I know that I could move all of the constants to a constants.py file and then I should be able to import it with from . import constants (as something) but I'm working on existing code and making that change would require a lot of refactoring. While that's not a bad idea, I'm wondering, given that Python does have a way to import individual objects, and also to import the whole module by name to an explicit name, if I can maybe do something with importlib to accomplish importing everything from __init__.py into a namespace?

fdmillion
  • 4,823
  • 7
  • 45
  • 82

2 Answers2

3

The loader sets __package__ which you can use:

import sys

SOME_CONSTANT_ONE = "one!" # We don't want to clobber this.

outer_module = sys.modules[__package__]

def do_work():
    print(SOME_CONSTANT_ONE)              # should print "one!"
    print(outer_module.SOME_CONSTANT_ONE) # should print "1"

This is precisely the attribute from which relative imports are based. See PEP 366 for details.

However, I really think the backwards-compatible refactoring which the other answer suggests is probably the better approach here.

wim
  • 338,267
  • 99
  • 616
  • 750
1

I could move all of the constants to a constants.py file and then I should be able to import it with from . import constants (as something) but I'm working on existing code and making that change would require a lot of refactoring

You can still refactor the constants into the new constants.py module. To support existing code relying on __init__.py you can import constants into the __init__.py

# constants.py
SOME_CONSTANT_ONE = 1
SOME_CONSTANT_TWO = 2
SOME_CONSTANT_THREE = 3
... # etc
# __init__.py
from .constants import *
# submodule.py
SOME_CONSTANT_ONE = 'dont clobber me!'
from . import constants as something
print(something.SOME_CONSTANT_ONE) # Yay namespaces
# existing_code.py
from . import SOME_CONSTANT_ONE
# still works!
# no refactor required!

Typically speaking, the __init__.py file is usually left completely empty and nothing is ever directly defined there. If there are contents in __init__.py they are typically imported from within the package. https://stackoverflow.com/a/4116384/5747944

sytech
  • 29,298
  • 3
  • 45
  • 86