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.
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.
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
.
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.