[Tutor] import * not allowed at function level - wassup?

Dave Angel davea at davea.name
Thu Jun 6 12:22:38 CEST 2013


On 06/06/2013 05:30 AM, Jim Mooney wrote:
> In the program below, which now works, I tried to use "from
> tkinter.constants import *" to throw up an error message with tkinter,
> and got the error "import * only allowed at module level." So I
> instead imported tkinter.constants, but then I had to prepend every
> constant with tkinter.constants, which is annoying. Is there a way
> around that prohibition (and what's it for, anyway?)

from xx import *

is generally discouraged, for a reason known as "namespace pollution." 
Once you've done that in a module, you no longer can tell from the 
source file where a particular symbol comes from.  Further, if you do it 
from more than one module, then the latter module hides symbols from the 
earlier one, invisibly.  If the modules change over time, then features 
you don't even use might stop features that you do from working properly.

Instead, try to use:

from xx import CONST1, CONST2, func3, func4, Class5

Here we can tell which constants, functions, and classes we're getting 
from the module, and can readily tell if there are any conflicts. 
Further, we can use the "as" notation to rename those that do create 
conflicts.

As for using the first form inside a function, it's probably useful to 
point out that locals inside functions are treated specially, and this 
special treatment leads not only to smaller compiled code, but faster 
(frequently much faster) code.  The compiler decides what all the local 
variables are for a given function, and gives each a number.  The number 
of slots is known when the function is entered, so a fixed structure can 
be used.  And each value is looked up at runtime by index (or slot 
number), and not by name.

You may have already seen the other effect of this, which is that you 
can't usefully add new entries to locals() inside a function.

So inside the function, use the same syntax shown above.  I don't use 
tkinter, so I may mess it up some.  But something like:

def die_on_errors():
     from tkinter.constants import BOTH, X
     #now BOTH and X are directly accessible, as local (constant) 
variables.  Without qualifiers.  Although X is a terrible name, perhaps 
it has some useful mnemonic in tkinter-speak.

Another comment, since you're referring to constants.  No reason that 
the function cannot refer to global constants, since it won't be doing 
any assignments to them, and therefore they don't need slots.  However, 
I hesitate to point that out, since it permits you to not only use the 
dreaded  'from xx import *' but it also separates reference to these 
arbitrary, undeclared names from their point of import.


>
> So I kludged the problem a bit by letting K = tkinter.constants, but
> I'd still just like to use the constants without any qualifiers, which
> I can do without a function. Except the error routine doesn't belong
> in the main program,

I hope you're not implying that you have a non-trivial "program" as 
top-level code ?? !!  Looking further, it appears you are.  Please learn 
to use

if __name__ == "__main__":
     doit()

or equivalent. Doesn't matter for toy projects, but sooner or later 
you're going to write some code you want to modify later, or reuse as a 
module.  At that point you'll wish it were already in functions.

Good rule of thumb is to include only the following kinds of things at 
top-level:

1) imports
2) the above if name==main thingie
3) calls to argv parsing
4) some help, in case argv stuff isn't right
5) calls to main(), whatever it's named

> IMHO, since it fuddles the main logic of getting
> a Fibonacci sequence. I want it out of the way and callable.
>
> (The message box appeared behind my editor, and was not visible, which
> drove me nuts, but it works fine at the dos prompt ;')
>
> #Using Python 3.3.2 on Win 7 - standard project, standard test file
> """Print a user-chosen Fibonacci sequence"""
> x = 0
> fib = [0,1]
> attempts = 0
>
> def die_on_errors():
>      """Throw up a message box and quit if too many bad entries are made"""
>      import tkinter
>      import tkinter.constants
>      K = tkinter.constants
>      tk = tkinter.Tk()
>      frame = tkinter.Frame(tk, relief=K.GROOVE, borderwidth=5)
>      frame.pack(fill=K.BOTH,expand=1)
>      label = tkinter.Label(frame, text="\n  Too many errors, quitting  \n  ")
>      label.pack(fill=K.X, expand=1)
>      tk.mainloop()
>
> user_prompt = "How long do you want your Fibonacci sequence?"
> user_error = "That's not an integer, please enter the desired integer length\
>   of your Fibonacci series again."
>
> while True:
>      try:
>          fiblong = int(input(user_prompt))
>          break
>      except ValueError:
>          attempts += 1
>          if attempts > 6:
>              die_on_errors()
>              exit('bye, bye')
>          user_prompt = user_error
>
> for cnt in range(0,fiblong - 2):
>      fib.append(fib[x] + fib[x+1])
>      x += 1
>

Why have 'x' here, when cnt is precisely the same thing, and is defined 
right here in the loop?

> print(fib)
>
>


-- 
DaveA


More information about the Tutor mailing list