Python rant: ‘ from foo import * ‘ is bad

August 11th, 2016 | Categories: programming, python | Tags:

This is my rant on import *. There are many like it, but this one is mine.

I tend to work with scientists so I’ll use something from mathematics as my example.  What is the result of executing the following line of Python code?

result = sqrt(-1)

Of course, you have no idea if you don’t know which module sqrt came from. Let’s look at a few possibilities. Perhaps you’ll get an exception:

In [1]: import math
In [2]: math.sqrt(-1)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
in ()
----> 1 math.sqrt(-1)

ValueError: math domain error

Or maybe you’ll just get a warning and a nan

In [3]: import numpy
In [4]: numpy.sqrt(-1)
/Users/walkingrandomly/anaconda/bin/ipython:1: RuntimeWarning: invalid value encountered in sqrt
#!/bin/bash /Users/walkingrandomly/anaconda/bin/python.app
Out[4]: nan

You might get an answer but the datatype of your answer could be all sorts of strange and wonderful stuff.

In [5]: import cmath
In [6]: cmath.sqrt(-1)
Out[6]: 1j
In [7]: type(cmath.sqrt(-1))
Out[7]: complex

In [8]: import scipy
In [9]: scipy.sqrt(-1)
Out[9]: 1j
In [10]: type(scipy.sqrt(-1))
Out[10]: numpy.complex128

In [11]: import sympy
In [12]: sympy.sqrt(-1)
Out[12]: I
In [13]: type(sympy.sqrt(-1))
Out[13]: sympy.core.numbers.ImaginaryUnit

Even the humble square root function behaves very differently when imported from different modules! There are probably other sqrt functions, with yet more behaviours that I’ve missed.

Sometimes, they seem to behave in very similar ways:-

In [16]: math.sqrt(2)
Out[16]: 1.4142135623730951

In [17]: numpy.sqrt(2)
Out[17]: 1.4142135623730951

In [18]: scipy.sqrt(2)
Out[18]: 1.4142135623730951

Let’s invent some trivial code.

from scipy import sqrt

x = float(input('enter a number\n'))
y = sqrt(x)

# important things happen after here. Complex numbers are fine!

I can input -1 just fine. Then, someone comes along and decides that they need a function from math in the ‘important bit’. They use import *

from scipy import sqrt
from math import *

x = float(input('enter a number\n'))
y = sqrt(x)

# important things happen after here. Complex numbers are fine!

They test using inputs like 2 and 4 and everything works (we don’t have automated tests — we suck!). Of course it breaks for -1 now though. This is easy to diagnose when you’ve got a few lines of code but it causes a lot of grief when there’s hundreds…or, horror of horrors, if the ‘from math import *’ was done somewhere in the middle of the source file!

I’m sometimes accused of being obsessive and maybe I’m labouring the point a little but I see this stuff, in various guises, all the time!

So, yeah, don’t use import *.

  1. August 11th, 2016 at 18:48
    Reply | Quote | #1

    Yeah, I don’t like those either.
    Thats why I avoid this by explicitly typing all namespaces.
    Both in Python and C++.

    It makes it easier to track where the functions are coming from.
    And thus helps readers of the code.
    Definitely not worth saving a few keystrokes by not typing ‘math.’ or ‘std::’ in the c++ case.

  2. brian t
    August 11th, 2016 at 20:23
    Reply | Quote | #2

    Totally agree – it was one of those things that confused me at first when reading others’ code, and it didn’t make sense when writing my own code. I don’t mind code being a little longer if it’s more logical and readable. I thought it was just me …

  3. Sam
    August 24th, 2016 at 01:34
    Reply | Quote | #3

    What an excellent rant on the subject of import *. This should be hyper-linked in every Python tutorial.

  4. August 25th, 2016 at 10:41
    Reply | Quote | #4

    I have the same aversion to using import package.* in Java, though nearly every book and tutorial uses it (I assume to save space).

  5. May 7th, 2019 at 04:36
    Reply | Quote | #5

    What about in an `__init__.py` file when you just want to pull all of the classes and functions from a submodule into the module namespace? It seems to me that in this situation `import *` has strong benefits (in particular, that it does not need to be updated every time you add something or change a name) and no serious drawbacks.