Python rant: ‘ from foo import * ‘ is bad
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 *.
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.
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 …
What an excellent rant on the subject of import *. This should be hyper-linked in every Python tutorial.
I have the same aversion to using import package.* in Java, though nearly every book and tutorial uses it (I assume to save space).
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.