Join us and get access to hundreds of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to hundreds of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Hint: You can adjust the default video playback speed in your account settings.
Hint: You can set the default subtitles language in your account settings.
Sorry! Looks like there’s an issue with video playback 🙁 This might be due to a temporary outage or because of a configuration issue with your browser. Please see our video player troubleshooting guide to resolve the issue.

Subpackages

In this lesson, you’ll learn about subpackages. Packages can contain nested subpackages to arbitrary depth. For starters, let’s make one more modification to the example package directory as follows:

Illustration of hierarchical file structure of Python packages

The four modules (mod1.py, mod2.py, mod3.py, and mod4.py) are defined as they were before. But now, instead of being lumped together into the pkg directory, they are split out into two subpackage directories: sub_pkg1 and sub_pkg2.

Importing still works the same as you saw before. Syntax is similar, but additional dot notation is used to separate the package name from the subpackage name:

>>>
>>> import pkg.sub_pkg1.mod1
>>> pkg.sub_pkg1.mod1.load_data()
loading data using mod1.load_data()
>>> from pkg.sub_pkg1 import mod2
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__',
'__spec__', 'help', 'mod2', 'pkg']
>>> mod2.clean_data()
cleaning data using mod2.clean_data()
>>> from pkg.sub_pkg2.mod3 import merge_data
>>> merge_data()
merging data using mod3.merge_data()
>>> from pkg.sub_pkg2.mod4 import Winner as Result
>>> x = Result()
>>> x
<pkg.sub_pkg2.mod4.Winner object at 0x108159588>

In addition, a module in one subpackage can reference objects in a sibling subpackage (in the event that the sibling contains some functionality that you need). For example, suppose you want to import and execute load_data() (defined in module mod1) from within module mod3. You can use an absolute import:

def merge_data():
    print('merging data using mod3.merge_data()')

class Message:
    pass

from pkg.sub_pkg1.mod1 import load_data
load_data()

Here’s what that looks like:

>>>
>>> from pkg.sub_pkg2 import mod3
loading data using mod1.load_data()
>>> mod3.load_data()
loading data using mod1.load_data()

Or you can use a relative import, where .. refers to the package one level up. From within mod3.py, which is in subpackage sub_pkg2:

  • .. evaluates to the parent package (pkg)
  • ..sub_pkg1 evaluates to subpackage sub_pkg1 of the parent package.

Here’s pkg/sub__pkg2/mod3.py:

def merge_data():
    print('merging data using mod3.merge_data()')

class Message:
    pass

from .. import sub_pkg1
print(sub_pkg1)

from ..sub_pkg1.mod1 import load_data
load_data()

Here’s what you get:

>>>
>>> from pkg.sub_pkg2 import mod3
<module 'pkg.sub_pkg1' (namespace)>
loading data using mod1.load_data()
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__',
'__spec__', 'help', 'mod3']

danny vw on March 4, 2020

Short question: if we have an additional level, e.g., grandparent_pkg/pkg1/sub_pkg2/mod3.py and suppose I need to go via the grandparent to reach some other modules in grandparent_pkg/pkg2/sub_pkg7/mod8.py. To go up to the grandparent from within mod3.py do we use then something like ../..pkg2.sub_pkg7/mod8.py ?

Chris Bailey RP Team on March 5, 2020

Hi @danny vw, I haven’t played around much with going that far backward. But it looks like instead of adding another pair of dots with a slash, you would add just one more dot for the next level up. So it would be from ...pkg2.sub_pkg7.mod8 import your_function. Two dots take you up to the parent, three dots to the grandparent.

karenson on May 15, 2020

I don’t understand why the last example only loads mod1 from sub_pkg1? Why isn’t mod2 loaded when you use “from .. import sub_pkg1”?

Playing around, sub_pkg1.mod1 is only loaded when you mod3 contains both “from .. import sub_pkg1” and “from ..sub_pkg1.mod1 import load_data”

Can you expound on what is causing this?

Chris Bailey RP Team on May 18, 2020

Hi @karenson,

I know that looks weird. What I have learned is that 2 non-obvious things are happening here. If you were to import one of the subdirectories by themselves, import pkg.sub_pkg1, it will only import the namespace and not the whole contents of the modules within. So, you must specify the module, not just it’s parents directory.

The second and probably weirder thing that happens, is that if you import only a portion of a module, like in the examples the function load_data, the other items from that module are imported also. So that’s why you can see if you check that the class Customer is also there. If you want to try an experiment, try importing into the REPL, from pkg.sub_pkg2.mod4 import send_mail, what you should find is the class Winner came along for the ride during that import. You can see the namespace pkg.sub_pkg2.mod4 has both.

Become a Member to join the conversation.