Jiang
Jiang

Reputation: 149

When does 'import' work recursively in Python

I am new to Python and the 'import' in Python is confusing me. It seems that it sometimes imports functions in the module recursively and sometimes doesn't.

For example, if I do

import numpy

then I can use all the functions in numpy, including the functions in the 'sub-modules' (not sure if it is a proper name), such as

a = numpy.fft.fft2(b)
c = numpy.random.rand()

However, if I do

import skimage

I can not use functions in the 'sub-modules' of skimage, such as

skimage.color.rgb2gray()
skimage.transform.resize()

I have to import the 'sub-modules' explicitly as

from skimage import color
from skimage import transform

So how to know whether import will work recursively for a module? And why does Python not make it consistent for all modules?

Upvotes: 0

Views: 569

Answers (1)

Brad Solomon
Brad Solomon

Reputation: 40888

What you're seeing isn't recursion. It's a case where some functions are being brought into the namespace of submodules.

From the skimage docs:

The main package of skimage only provides a few utilities for converting between image data types; for most features, you need to import one of the following subpackages ...

In short, color is not a module; its a fully bonafide package-within-a-package. A package's __init__.py defines what gets brought into its namespace. When you see "skimage has no attribute 'color'", that's because the top-level package skimage only has these things available:

[f for f in dir(skimage) if not f.startswith('_')]

['data_dir',
 'doctest',
 'doctest_verbose',
 'dtype_limits',
 'img_as_bool',
 'img_as_float',
 'img_as_int',
 'img_as_ubyte',
 'img_as_uint',
 'pkg_dir',
 'test',
 'test_verbose',
 'util']

Now, check out the __init__.py from NumPy:

from . import core
from .core import *
from . import compat
from . import lib
from .lib import *
from . import linalg
# ... and so on

(Nothing equivalent is being done in the skimage __init__.py)

All of these sub-packages are now fully available with attribute dot-notation access; and, all of the functions from .lib are now also acccessible via np.<function> because they are imported here as well.

You can see this difference for yourself with a simple nested package structure. Make this on your local drive:

sound/
    __init__.py
    subpackage1/
        __init__.py
        submodule1.py
    subpackage2/
        __init__.py
        submodule2.py

Now, in the top-level __init__.py, put just from sound.subpackage1 import submodule1. (Don't reference subpackage2.) Now if you cd into one level above sound, and do import sound, try using tab completion. You'll see that submodule1 and subpacakge1 are directly available with dot notation, but the other sub-package is not.

Here's one other note: the full path to rgb2gray is actually skimage/color/colorconv/rgb2gray. Why is it accessible from color? Because it's imported within the __init__.py of color.

To your last question:

So how to know whether import will work recursively for a module? And why does Python not make it consistent for all modules?

There's not a lot of predictable consistency between packages regarding whether they make these imports internally. Typically, large "nested" libraries such as pandas and NumPy do. In the case of skimage, its designers might have wanted to keep its subpackages "distinct" because some of them offer very different functionality, even if they are image-related.

Upvotes: 3

Related Questions