[SciPy-Dev] lazy imports feedback request

Paul Ivanov pivanov314 at gmail.com
Thu Sep 29 01:18:38 EDT 2011


Friends and uncles,

I'm seeking feedback about some lazy import functionality I'm trying to
implement in nitime. In short, "foo = LazyImport('foo')" returns something that
will act as foo if you did just "import foo" - but only perform the import when
foo is actually used, not when it is defined.

I'll outline the highlights in this email, but you can see the full four-leaf
clover pull request discussion here: https://github.com/nipy/nitime/pull/88

Here's the code, with an explanatory docstring

class LazyImport(object):
    """
    This class takes the module name as a parameter, and acts as a proxy for
    that module, importing it only when the module is used, but effectively
    acting as the module in every other way (including inside IPython with
    respect to introspection and tab completion) with the *exception* of
    reload().

    >>> mlab = LazyImport('matplotlib.mlab')

    No import happens on the above line, until we do something like call an
    mlab method or try to do tab completion or introspection on mlab
in IPython.

    >>> mlab
    <module 'matplotlib.mlab' will be lazily loaded>

    Now the LazyImport will do an actual import, and call the dist function of
    the imported module.

    >>> mlab.dist(1969,2011)
    42.0
    >>> mlab
    <module 'matplotlib.mlab' from '.../site-packages/matplotlib/mlab.pyc'>
    """
    def __init__(self, modname):
        self.__lazyname__= modname
    def __getattribute__(self,x):
        # This method will be called only once
        name = object.__getattribute__(self,'__lazyname__')
        module =__import__(name, fromlist=name.split('.'))
        # Now that we've done the import, cutout the middleman
        class LoadedLazyImport(object):
            __getattribute__ = module.__getattribute__
            __repr__ = module.__repr__
        object.__setattr__(self,'__class__', LoadedLazyImport)
        return module.__getattribute__(x)
    def __repr__(self):
        return "<module '%s' will be lazily loaded>" %\
                object.__getattribute__(self,'__lazyname__')

The win is that when you get a faster import and smaller memory footprint by
not loading those modules which your code path ends up not using.

The functionality of lazyimports.LazyImport is generic enough to allow the
lazily imported module to act as the module in almost every way (tab
completion, introspection for docstrings and sources) except reloading is
not supported.

Although I have not spent a lot of time trying to make reloading work, I think
it is not a big deal because here in the intent is to lazily load external
packages (matplotlib, scipy, and numpy's nosetools) which are not meant to be
developed simultaneously with the project wishing to load them in a lazy
manner.

A difference with another strategy, such as one Keith Goodman highlighted in a
thread announcing Bottlneck 0.4.1 on [SciPy-User] back in March which
looks like this:

# http://wiki.python.org/moin/PythonSpeed/PerformanceTips#Import_Statement_Overhead
email = None
def parse_email():
   global email
   if email is None:
       import email

is that LazyImport keeps you from having to check if a given module
has been lazily
loaded in every place that it is used.

Fernando Perez suggested I contact this list because he recalls similar
functionality being implemented in scipy at some point, and then removed
because it was problematic.

thanks a lot in advance for any feedback. I'm cc-ing my primary
address in hope that your mail reader will do the same.

best,
--
Paul Ivanov



More information about the SciPy-Dev mailing list