NB question on global/local variables in functions

skip at pobox.com skip at pobox.com
Sat Jul 15 09:25:00 EDT 2006


    Wolfgang> thanks for the hint! But what is the difference between
    Wolfgang> from module import *
    Wolfgang>   and
    Wolfgang> import module
    Wolfgang> ?

Try it and see.  I happen to have a.py laying about:

    import atexit

    def work():
      print "whew! work is hard"

    def exit():
      print "module a is cleaned up"

    atexit.register(exit)

At the top level there are three names, atexit, work and exit.
When I first start the interpreter I don't have much available:

    >>> vars().keys()
    ['__builtins__', '__name__', '__doc__']

If I import the module a, a reference to a is added to the namespace, but
not (directly) the objects it contains:

    >>> import a
    >>> vars().keys()
    ['__builtins__', '__name__', '__doc__', 'a']

I can still get at the objects in a though:

    >>> dir(a)
    ['__builtins__', '__doc__', '__file__', '__name__', 'atexit', 'exit', 'work']
    >>> a.work
    <function work at 0x3c9570>

Get rid of a:

    >>> del a
    >>> vars().keys()
    ['__builtins__', '__name__', '__doc__']

Now import everything in a:

    >>> from a import *
    >>> vars().keys()
    ['__builtins__', 'work', 'atexit', 'exit', '__name__', '__doc__']

There are two practical differences to importing names from a module
vs. just importing the module.  As I indicated in my previous note, the
"from star" import pollutes your namespace.  It might not seem like much if
you're only grabbing three objects you are interested in, but imagine if a
contained a lot of names, many of which you weren't going to use in your
program:

    >>> from Tkinter import *
    >>> vars().keys()
    ['getdouble', 'MULTIPLE', 'TypeType', 'mainloop', 'Canvas', 'AtSelLast',
    'CodeType', 'TRUE', 'getboolean', 'LAST', 'TclVersion', 'BOTTOM', 'Wm',
    'NUMERIC', 'Toplevel', 'DictProxyType', 'ObjectType', 'DictType',
    'EXTENDED', 'OFF', 'SEL', 'LongType', 'CURRENT', 'CallWrapper',
    'Scrollbar', 'ListType', 'X', 'GeneratorType', 'FIRST', 'ON',
    'image_names', 'ClassType', 'YES', 'LambdaType', 'GROOVE', 'XRangeType',
    'Scale', 'NORMAL', 'SW', 'BUTT', 'Label', 'ROUND', 'image_types',
    'AtInsert', 'StringType', 'NONE', 'CENTER', 'FloatType', 'Spinbox',
    'UnicodeType', 'Checkbutton', 'Grid', 'StringTypes', 'ModuleType',
    'FileType', 'FLAT', 'END', 'VERTICAL', '__builtins__', 'MITER',
    'Widget', 'DISABLED', 'S', 'COMMAND', 'EllipsisType', 'W', 'ACTIVE',
    '__name__', 'EW', 'FrameType', 'BASELINE', 'CHORD', 'tkinter',
    'FunctionType', 'Image', 'BitmapImage', 'Event', 'RADIOBUTTON',
    'SliceType', 'Place', 'HIDDEN', 'PAGES', 'NoDefaultRoot', 'ANCHOR',
    'CHAR', 'SEPARATOR', 'BooleanType', 'HORIZONTAL', 'TclError', 'MOVETO',
    'WORD', 'SUNKEN', 'NO', 'DictionaryType', 'NotImplementedType',
    'READABLE', 'NE', 'CHECKBUTTON', 'Variable', 'Pack', '__doc__', 'NW',
    'RAISED', 'AtEnd', 'RIDGE', 'BooleanVar', 'Tributton', 'SOLID', 'N',
    'CASCADE', 'SEL_FIRST', 'TkVersion', 'UNDERLINE', 'UNITS', 'TupleType',
    'OptionMenu', 'ALL', 'NS', 'FALSE', 'Text', 'Frame', 'SEL_LAST', 'Misc',
    'OUTSIDE', 'LabelFrame', 'getint', 'Radiobutton', 'LEFT', 'Listbox',
    'wantobjects', 'SE', 'EXCEPTION', 'IntType', 'Menu', 'TOP', 'DoubleVar',
    'DOTBOX', 'SINGLE', 'Tk', 'IntVar', 'AtSelFirst', 'UnboundMethodType',
    'PanedWindow', 'INSERT', 'BuiltinMethodType', 'BROWSE', 'Tcl',
    'BuiltinFunctionType', 'BaseWidget', 'PROJECTING', 'MethodType',
    'TracebackType', 'BEVEL', 'E', 'InstanceType', 'BOTH', 'PIESLICE',
    'Button', 'sys', 'Y', 'Entry', 'Message', 'PhotoImage', 'RIGHT',
    'BufferType', 'Studbutton', 'INSIDE', 'Menubutton', 'WRITABLE',
    'StringVar', 'ARC', 'At', 'ComplexType', 'NSEW', 'SCROLL', 'NoneType'] 

The chance that one of those names would rebind an already existing name in
your namespace is much higher.

The second practical difference is that if the object referenced by a name
is immutable (say, an integer), the name in the current namespace doesn't
get rebound if the module modifies the value.  Consider b.py:

    total_work = 0
    def work():
      global total_work
      print "not as hard as it might be"  
      total_work += 1

If I execute

    from b import total_work, work

names are created in my current namespace that reference the objects bound
to those names *at that moment* in the b module's namespace.  If I then call
work(), it rebinds the name total_work in b's namespace (because integers
are immutable), however, I don't see the effect, because the name
"total_work" in my namespace is still bound to the object 0:

    >>> from b import total_work, work
    >>> total_work
    0
    >>> work()
    not as hard as it might be
    >>> total_work
    0
    >>> work()
    not as hard as it might be
    >>> total_work
    0

Apparently work is really pretty easy...

If, on the other hand, I import b, I get the expected behavior:

    >>> import b
    >>> b.total_work
    0
    >>> b.work()
    not as hard as it might be
    >>> b.total_work
    1
    >>> b.work()
    not as hard as it might be
    >>> b.total_work
    2

because each time I reference b.total_work I'm using the name in b's
namespace to find the current object to which it's bound.  That's the same
name that b.work() rebinds each time it's called.

Skip



More information about the Python-list mailing list