Imported globals?

anethema jefishman at gmail.com
Sat Jul 28 05:25:35 CEST 2007


> It seems that in the first case change_value() called in module_b.py
> ignores the global statement. Is it so? Why? What happens in the second
> case? I really don't get it.

The key is that it doesn't ignore the global statement, but that
global specifically points to the variable 'value' in module A's
namespace.  That is, even though you call change_it() from module B,
it acts on the variable in module A, not B, even though 'value' exists
in B.

consider the following modules:

module_a.py
    value = 'initial'

    def change_it():
        global value
        value = 'changed'

    def print_it():
        print "In module A:", value

    print_it()

module_b.py
    from module_a import value, change_it, print_it as a_print_it()

    def print_it():
        print "In module B:", value

    print_it()
    change_it()
    a_print_it()
    print_it()


$> python module_b.py
In module A: initial
In module B: initial
In module A: changed
In module B: initial

Here we see that a) print_it() was executed in module A before the
code in module B ran; This happens at import.  b) after change_it()
was executed, 'value' in module A was changed, while 'value' in module
B was left alone.

This demonstrates how the modules have separate namespaces, but also
hinges on the fact that strings are immutable, as star.public
mentions.  'value' in both namespaces do refer to the same object.
What's significant is the action on the method change_it().  For
example,

def change_it():
    value = 'changed'

this would do no good at all, as you may have realized, as it merely
assigns the string 'changed' to the name/variable 'value' in the
function namespace.  If there is an assignment to a variable in a
function (not an attribute of some other variable), such as 'a = 2',
the interpreter assumes that the function is declaring a new
variable.  We need to explicitly declare the variable global first.
If we don't, and try to treat it global, the above useless function
can result, or the following

def change_it():
    if value == 'initial':
        value = 'changed'

which results in a UnboundLocalError, since, defaulting to using
'value' as a local variable (since we have the assign statement `value
= 'changed'`), and we attempt to use the local var before assigning it
a value.

For mutable objects, such as a list, or classes (and their
attributes), the result would be different.

module_a.py
    value = ['initial']

    def change_it():
        value[0] = 'initial' # don't need the 'global value', since
we're accessing attributes
                             # of 'value', not directly accessing
value

    def print_it():
        print "In module A:", value[0]

    print_it()

module_b.py
    from module_a import value, change_it, print_it as a_print_it

    def print_it():
        print "In module B:", value[0]

    print_it()
    change_it()
    a_print_it()
    print_it()

$> python module_b.py
In module A: initial
In module B: initial
In module A: changed
In module B: changed


In this case, the two module namespaces still each contain a reference
to the same object.  What we did differently is change the object
itself, instead of the reference.  If change_it() assigned to value,
`value = ['changed']`, we would see no different behavior than in the
first case.


Sorry if that was overly winded.  scope and namespaces are tricky (at
least for me) to explain.
  - Jeremy




More information about the Python-list mailing list