A slightly unobvious trap with 'from module import *'

March 31, 2011

If you are already being lazy, it is easy to drift into the habit of doing 'from yourmodule import *' in your own code, especially in situations where you really are going to use everything from your module in the code that you're writing (for example, importing your Django app's models into your view code for it). As it happens, there is a little trap for you waiting here.

When they write 'from <X> import *', most people are probably thinking that it imports everything they've defined in module X into their current module. But this is not quite what it does. What it actually does is that it imports everything in module X's namespace into your namespace. Now, most of what's in module X's namespace is what you defined in it. But the namespace also includes whatever you imported in module X; all of those things have now been silently imported into your namespace too.

(Yes, yes, __all__ can cut this off. I don't think people use __all__ very much for internal modules and internal imports like this, unless they are the sort of person who eschews 'from <X> import *' in the first place.)

At this point it's quite easy to just start using some of these silently imported things in your code without realizing that you haven't explicitly imported them. When dealing with a bunch of directly imported things (eg, 'from what.util import SomeThing') and a bunch of your own code, it's easy to lose track of what you have and haven't imported yet. When you write the code and it works, you're of course going to assume that you explicitly imported whatever you're using, because that's the only way the code could work, right?

Now the problem is that in your code in module Y is quietly depending on the inner workings of module X. If you revise module X so that it no longer needs the SomeThing utility bit and so remove the import, X will still work but suddenly this other module Y breaks. Oops.

(The extreme case of this is forgetting to import an entire module. Then you might wind up with all sorts of code in module Y making free use of, say, 'socket.<whatever>', despite there being nary an 'import socket' to be seen. This is the sort of thing that leaves you starting at the revision history in your version control system and wondering how the code ever worked in the first place.)

A more perverse version of this has intermediate steps; module X is imported wholesale into module Y, which is in turn imported wholesale into module Z, which is where you accidentally use SomeThing without importing it. After this happens, there's a whole raft of changes that can be done that break module Z.

(I'm sure that your imagination can come up with even more extreme and odd scenarios.)

Written on 31 March 2011.
« A really annoying gap in system observability
How to use gdb to call getpeername() »

Page tools: View Source, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Thu Mar 31 23:10:09 2011
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.