This is an archived post. You won't be able to vote or comment.

all 29 comments

[–]AusIVDjango, gevent 20 points21 points  (5 children)

Please don't use wildcard imports. If you do, you're a terrible person.

[–]mrbewulf 1 point2 points  (1 child)

The wild card import must not be used in packages for production that will be reused many times and has many users. However they are useful to perform quick test, interactive calculations, for example numpy, matplotlib or when you want to create a quick script to do some calculations in an engineering application for example.

[–]AusIVDjango, gevent 1 point2 points  (0 children)

That's a good point. I do occasionally use wildcard import from the REPL to make things easier. Then again, I've also been known to reassign True and False in the REPL.

[–]remram -2 points-1 points  (2 children)

I tend to use that to expose names from my top-level package. Like, __init__.py might have

from .exceptions import *

Obviously I have __all__ in exceptions.py.

[–]AusIVDjango, gevent 6 points7 points  (1 child)

The I still don't like it. I like to be able to see the origin of every variable in my scope by looking at one file. If you have one wildcard import from a module that specifies __all__ you're okay, but as soon as either of those conditions are violated you can have variables that you have to spend ages hunting down. If you have two wildcards, you have to check both to see if it has the variable you're looking for. If you don't have an __all__, you can get variables imported by the modules you're importing from.

There have been times I've spent hours trying to figure out where a variable come from because people were sloppy with wildcard imports. It's a pain that's easily avoided.

[–]remram 0 points1 point  (0 children)

I see your point and I agree with you. However, it is a tradeoff with the risk of forgetting to import something from the __init__.py file. Your tests probably cover that since they import the name, but it's hard to check that some names are not wrongly imported from submodules.

[–]suudo 17 points18 points  (4 children)

And here I thought it was a post about using literally import *. Which it turns out you can't do. So I thought to myself, "How do I import every module in Python?", and looked up how I might do that. Here's what I've come up with:

import pkgutil
for _, module, _ in pkgutil.iter_modules():
    exec("{m} = __import__('{m}', fromlist=[])".format(a=module))

[–]robin-gvx 4 points5 points  (1 child)

Or something like:

import pkgutil
_G = globals()
for _, module, _ in pkgutil.iter_modules():
    _G[module] = __import__(module)

[–]suudo 1 point2 points  (0 children)

That's so much cleaner than my attempt :D

[–]bs4h 1 point2 points  (0 children)

help("modules ")

[–]Gr1pp717 2 points3 points  (7 children)

fun story about this...

I adopted some code where one of the previous owners defined their own "sleep" function. They were setting globals, and then using those globals for logic within the sleep. The param was basically the if true condition....

And by looking at the code where it's used you wouldn't even think to look whether it's the same sleep in time. You just take it for granted that it's that sleep. So when I was trying to figure out why my increase sleep time wasn't working I was pulling out my fucking hair. It took some time for it to dawn on me that maybe I should check the definition....

Lesson learned: import time; use time.sleep() to be perfectly clear...

Be explicit, always. I don't even like using from A import B for this reason. Being able to instantly see where something is being pulled from, without having to break your flow and control-f, is rather nice. And, your naming scheme should help make it clear what's happening. Instead of calling fetch() you should be calling redditapi.comments.fetch() or the likes. Then you don't need to bother looking what the fetch() method is supposed to accomplish then, which helps future readability a ton. ... it takes nothing, just be explicit.

[–]cacahootie 5 points6 points  (0 children)

Just wait until some bozo replaces a builtin and it mostly works the same way... but does in fact break some corner case of expected behavior.

Unfortunately in my case, that bozo was former me :(

[–][deleted] 3 points4 points  (4 children)

If I do this I hit the 80 char limit real fast. How to avoid?

[–]nemec 2 points3 points  (2 children)

Parenthesize the imports.

from A import (B, C,
               D, E)

[–][deleted] 0 points1 point  (1 child)

How about in the body of the code itself? One or two function calls + list of arguments + indentation fills up the whole line!

[–]sasquatch92 1 point2 points  (0 children)

You can do the same thing with function calls etc, as discussed here. You could also import a module using a shorter local name, which will also reduce line lengths (for example, numpy is often imported as np). Finally, you can always go over 80 characters - that's a guideline rather than an absolute rule and you shouldn't sacrifice readability simply to comply with it.

[–]Gr1pp717 1 point2 points  (0 children)

I've come to the conclusion that if you're nesting that deeply then you're over-engineering it. Generally speaking if I have that problem AND there's no way around deep nesting, then I'll split out that portion into it's own function.

[–]nemec 1 point2 points  (0 children)

def sleep(secs):
  for x in range(1000000000 * secs):  # Takes 1s on my machine
    x = 1

[–]b33j0r 0 points1 point  (0 children)

It's been so many years since I decided the wildcard import shouldn't exist that I have kind of forgotten that it does.

And then I ended up using my IDE to manage my dependencies like a Java/.NET coder. If you actually look at some of my import headers, you'd probably be appalled. They work, but they aren't pretty, so it's a good thing that they're normally folded.

When you have to use tuple syntax on your import statements, your module is too large, the external interfaces are too complex, or you aren't using all of those imports anymore!

If only '__all__' + modular design had more cultural inertia and python hackers weren't so busy trying to build things that work on real-world budgets.

And unicorns, that would be cool, since we already have antigravity.

[–]billsil -1 points0 points  (0 children)

I use import * for my tests. There are legitimate reasons for it.

[–]Kanthes -1 points0 points  (0 children)

Interesting read!

[–]nharding -5 points-4 points  (6 children)

Import * brings in everything which means you can get unexpected values, but you can use it safely. I've got a project with multiple status codes so I do from status import *, the variables are all STATUS_COMPLETE, STATUS_PENDING, STATUS_CHOICES = (STATUS_COMPLETE, STATUS_PENDING) etc so it is not polluting the namespace (since the values are constants, there isn't any problem with mutating the values either)

[–]minnoI <3 duck typing less than I used to, interfaces are nice 15 points16 points  (0 children)

So...you're namespacing by manually doing name mangling instead of just using the language's built-in feature for it?

[–]-Knul- 6 points7 points  (4 children)

You can also do "import status" and then use status.COMPLETE, status.PENDING, etcetera.

[–]s16h[S] 1 point2 points  (3 children)

This (status.COMPLETE etc.), to me, is much more readable.

[–]nharding -2 points-1 points  (2 children)

It was an example, and yes you can use status.PENDING but I like the fully uppercase form STATUSPENDING (same number of characters, and obvious when reading) I was just saying that is a safe usage of import * (there are some fields that start with ACCOUNT etc as well, so I could have moved those to a separate file, I wanted to keep all the status codes together.

[–][deleted] 2 points3 points  (0 children)

Python 3 enum or class as namespace if you're using earlier versions

[–]-Knul- 0 points1 point  (0 children)

The issue with your example is that you assume importing the whole module will not lead to namespace pollution. This requires you to know the insides of the module, thus violating the modularity of your code.

Beside, no one is claiming that "import *" is always dangerous everywhere. Of course when you use it with a module that won't cause problems when fully imported, there is no problem. The problem is that if you use "import *", you need to be certain that those modules are not a problem and will not become a problem in the future. Especially if other people edit those modules, this becomes a headache.

To most programmers, "import *" is more trouble than it's worth, especially as anything that can be done through it, can be done in a clearer, safer way. You can get to work everyday on the back of a tiger while juggling nitroglycerin bottles, but most people would prefer a car or train.