4 ms·
Some package developers don't want their users to have the two step process of "import" than "use." NumPy imports 137 modules with "import numpy", of which 94 a
by dalke 4y ago
Some package developers don't want their users to have the two step process of "import" than "use." NumPy imports 137 modules with "import numpy", of which 94 are specifically in the NumPy hierarchy:
>>> import sys
>>> len(sys.modules)
83
>>> import numpy as np
>>> len(sys.modules)
220
>>> sum(1 for k in sys.modules if "numpy" in k)
94
so people can write a one-liner like:
>>> np.polynomial.chebyshev.Chebyshev([0,1,3])(np.linspace(-1.0, 1.0, 5))
array([ 2., -2., -3., -1., 4.])
without having to import np.polynomial.chebyshev.Chebyshev first.
This API design requires importing most of NumPy at startup, which has a cost they didn't consider so important because their users are primarily doing long-term computing and notebook-style development, where startup cost is relatively small.
I've complained about this because I live in the short-lived program world, where it's annoying to have a 0.1 second import overhead if I only need one function from NumPy:
py310% time python -c 'pass'
0.025u 0.006s 0:00.03 66.6% 0+0k 0+0io 0pf+0w
py310% time python -c 'import numpy'
0.142u 0.292s 0:00.14 307.1% 0+0k 0+0io 0pf+0w
As I understand it, SciPy wants a similar API design goal, but has a lot more packages. They've developed lazy imports to try to have the best of both worlds.
> For command line utilities, it seems like you're going to need to load the module at some point or another regardless (if you're actually using it)
Thing is, you might not actually use it. If the command-line tool uses subcommands, each different subcommand might need only a subset of the full set of packages.
Perhaps only one of the subcommands uses NumPy, while for 95% of uses, NuPy isn't used at all.
As the discussion for this feature points out, this can be addressed by only importing when needed. (One of the reasons I've started using click over argparse is click does more of this separation for me.) However, it's somewhat fragile, in that it's easy to add an rarely-needed expensive import at top-level without noticing it, and requires some non-standard tooling to detect issues, like the non-predictability you mentioned.
I personally want something like the lazy-/auto- importer in my package, so I can reduce the two step process. My last package released used module-level getattr functions, which gets me mostly there, except for notebook auto-completion of the lazy wrappers. (It works in the command-line shell though.)
I can't import everything on startup because parts of my package depend on third-party packages, which might not be installed. I instead want to raise an ImportError when those lazy objects are accessed. Plus, one of the third-party packages is through a Python/Java bridge, which has its own startup costs that I want to avoid.
- slaymaker1907 4y agoYou could do the numpy style API lazily. They would just need to each API as an object that does the imports dynamically.
- dalke 4y ago... yes. The PEP describes existing solutions, followed by a proposal for "a more general and comprehensive solution for lazy imports that can encompass all of the above use cases and does not impose detectable overhead in real-world use", to quote the PEP at https://peps.python.org/pep-0690/ https://peps.python.org/pep-0690/ .