'Global variable imported from a module does not update - why?

I'm having trouble understanding why importing a global variable from another module works as expected when using import, but when using from x import * the global variable doesn't appear to update within its own module

Imagine I have 2 files, one.py:

def change(value):
        global x
        x = value

x = "start"

and two.py:

from one import *
print x
change("updated")
print x

I'd expect:

start
updated

But I get...

start
start

If I import the module normally it works as expected

import one
print one.x
one.change("updated")
print one.x

Result...

start
updated

Given that I can't change ony.py's use of global variables (not my fault), and two.py is meant to be a sort of wrapper* around one.py, I'd really like to avoid using the one. namespace throughout two.py for the sake of one stubborn variable.

If it's not possible a novice-level explantion of what's going on might help me avoid getting stuck like this again. I undertand that one.x is getting updated, but two.x isn't respecting the updates, but I don't know why.



Solution 1:[1]

You can think of a package as a dict. Every function and variable in a package is listed as a key in that dict which you can view using globals().

When you import an object from another package, you copy a reference to the object into your own package under a name (usually the same, different if you import <var> as <name>).

By setting the value in the other package, you overwrite the key in that package with a new value, but you leave your reference pointing to the old one.

An analogy with dicts

We can demonstrate the process using dicts as an analogy:

# Our 'packages'
one = {'x': 'start'}
two = {}

# Equivalent of our import
two['x'] = one['x']   

# Variable updated in `one'
one['x'] = 'updated'

# ... and accessed in `two`
print(two['x'])   # Still 'start'

We would not expect the two dict to be updated just because we overwrote a value in one.

What you can do about it

You can modify the object as long as you don't break the pointer by overwriting the variable. For example if x was a dict, you could change a value inside the dict and the two variables would still point to the same object.

Alternatively you could attach a variables to the function like this:

def change(value):
    change.x = value

This does the same work by ensuring we are mutating the same object.

A better answer yet might be to wrap both items in an object if they need to travel together:

class Changer:
    x = 'start'

    @classmethod
    def change(cls, value):
        cls.x = value

At this point however, we could modify the value directly as an attribute:

Changer.x = 'updated'

Which might be the simplest.

Solution 2:[2]

You can import twice from the same module like this:

from one import *
import one
print one.x
change("updated")
print one.x

basically by attaching module name you are forcing the code to use the imported module's variable.

Solution 3:[3]

This is probably the best you can do inside two.py:

import one
from one import x

def change(value):
    one.change(value)
    global x
    from one import x

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Jon Betts
Solution 2 KawaiKx
Solution 3 Alex Hall