Can't get the changed global variable
Question:
t.py
value = 0
def change_value():
global value
value = 10
s.py
import t
from t import value
t.change_value()
print(f'test1: {t.value}')
print (f'test2: {value}')
Output
test1: 10
test2: 0
Why isn’t it not returning the changed value in the test2 ?
Answers:
This is because you’re importing value
before updating it. Here’s what your code does:
- Imports the
t.py
and then t.value
- Runs the
t.change_value()
function which makes value = 10
.
- Prints
t.value
and value
.
Now note, your value
is already imported in the start which was 0. Therefore, if you want value
to be equal to t.value
, then you need to import it after updating. Yes, it’s a headache but this is how Python works.
Updated Code:
import t
t.change_value()
from t import value
print(f'test1: {t.value}')
print (f'test2: {value}')
Output:
test1: 10
test2: 10
However, if you don’t want to import later on. You can make change_value()
return value
and update it as well, so that it’s accessible in t.py
and s.py
simultaneously.
Recommend Code:
import t
from t import value
value = t.change_value()
print(f'test1: {t.value}')
print (f'test2: {value}')
Both give the same output
This is how import
works in Python. You should read what the documentation says on the import
statement carefully.
Specifically, when you use the from import
syntax on a module attribute, Python looks up the value of that attribute, and then "a reference to that value is stored in the local namespace".
So, if value
is 10
, then after from t import value
you have a reference to 10
. That 10
stays a 10
no matter what happens to t.value
.
Just to drive home the point about reference semantics vs. value semantics, consider what would happen if you had this in t.py
instead:
value = []
def change_value():
global value # not actually needed anymore
value.append(10)
def change_reference():
global value
value = value + [10]
Contrast the differences between calling t.change_value()
and t.change_reference()
. In the first case, both prints would be the same, while in the second, they would be different.
This is because after calling change_value()
we only have one list, and the name value
in s.py
is referring to it. With change_reference()
we have two lists, the original (empty) one and a new one, and value
in s.py
is still referring to the original one.
I think the main point of confusion is that with from t import value
you are not importing a complex module or another reference to a mutable object, but a simple immutable int
(in this case). In a way, from t import value
is about the same as value = t.value
(assuming you already imported t
before), storing the value of t.value
in another variable (also called value
). Assigning a new value to t.value
won’t change the value assigned to value
.
Counterexample:
t.py
value = [1000]
def change_value():
global value
value[0] = 1001
s.py
import t as t
from t import value
t.change_value()
print(f'test1: {t.value[0]}')
print (f'test2: {value[0]}')
Output:
test1: 1001
test2: 1001
Ok, so what is going on?
Integers are immutable, which means when defined, it is no longer possible to change what value given object represents. When you reassign an integer, you are really creating a new int
object and changing the reference to point to this new object.
Which leads us to observed behaviour: t.value
and value
are 2 separate variables – initially, they point to the same address. But when we change one of them, Python cannot change the actual value so it changes the reference. And because reference is bound to a variable, it changes for only one of them.
Using mutable types like int lets us change its content without changing the reference, so both variables still point to the same object and changes are visible for both.
Imagine you have someone’s number saved in your phone.
### In your phone:
Alice: 312 3456789
Bob: 614 7984321
Your friend Carol has some other numbers in her phone.
### In Carol's phone
Dave: 602 9406981
Erin: 216 3714732
After the dinner party, Carol asks you for Alice’s phone number, and you share it with her. Now:
### In your phone:
Alice: 312 3456789
Bob: 614 7984321
### In Carol's phone
Dave: 602 9406981
Alice: 312 3456789
Erin: 216 3714732
At some later date, Alice tells you "Hey, I’m changing phone numbers, here’s the new number" so you update your phone:
### In your phone:
Alice: 312 1111111 <--- Alice's new number
Bob: 614 7984321
### In Carol's phone
Dave: 602 9406981
Alice: 312 3456789
Erin: 216 3714732
This doesn’t magically update Carol’s phone, so she’s still got the old number. You can’t actually edit a number, it’s immutable, the thing you updated here is a phone directory – a mapping of names to numbers.
Modules in Python are analogous to this situation. Module t
has a namespace, t.__dict__
mapping names to values. Module s
also has a namespace s.__dict__
. The function t.change_value
only updates an entry for the name "value" in its own "phone directory" i.e. in t.__dict__
. There is no mechanism for that action to update other unrelated namespaces at the same time.
t.py
value = 0
def change_value():
global value
value = 10
s.py
import t
from t import value
t.change_value()
print(f'test1: {t.value}')
print (f'test2: {value}')
Output
test1: 10
test2: 0
Why isn’t it not returning the changed value in the test2 ?
This is because you’re importing value
before updating it. Here’s what your code does:
- Imports the
t.py
and thent.value
- Runs the
t.change_value()
function which makesvalue = 10
. - Prints
t.value
andvalue
.
Now note, your value
is already imported in the start which was 0. Therefore, if you want value
to be equal to t.value
, then you need to import it after updating. Yes, it’s a headache but this is how Python works.
Updated Code:
import t
t.change_value()
from t import value
print(f'test1: {t.value}')
print (f'test2: {value}')
Output:
test1: 10
test2: 10
However, if you don’t want to import later on. You can make change_value()
return value
and update it as well, so that it’s accessible in t.py
and s.py
simultaneously.
Recommend Code:
import t
from t import value
value = t.change_value()
print(f'test1: {t.value}')
print (f'test2: {value}')
Both give the same output
This is how import
works in Python. You should read what the documentation says on the import
statement carefully.
Specifically, when you use the from import
syntax on a module attribute, Python looks up the value of that attribute, and then "a reference to that value is stored in the local namespace".
So, if value
is 10
, then after from t import value
you have a reference to 10
. That 10
stays a 10
no matter what happens to t.value
.
Just to drive home the point about reference semantics vs. value semantics, consider what would happen if you had this in t.py
instead:
value = []
def change_value():
global value # not actually needed anymore
value.append(10)
def change_reference():
global value
value = value + [10]
Contrast the differences between calling t.change_value()
and t.change_reference()
. In the first case, both prints would be the same, while in the second, they would be different.
This is because after calling change_value()
we only have one list, and the name value
in s.py
is referring to it. With change_reference()
we have two lists, the original (empty) one and a new one, and value
in s.py
is still referring to the original one.
I think the main point of confusion is that with from t import value
you are not importing a complex module or another reference to a mutable object, but a simple immutable int
(in this case). In a way, from t import value
is about the same as value = t.value
(assuming you already imported t
before), storing the value of t.value
in another variable (also called value
). Assigning a new value to t.value
won’t change the value assigned to value
.
Counterexample:
t.py
value = [1000]
def change_value():
global value
value[0] = 1001
s.py
import t as t
from t import value
t.change_value()
print(f'test1: {t.value[0]}')
print (f'test2: {value[0]}')
Output:
test1: 1001
test2: 1001
Ok, so what is going on?
Integers are immutable, which means when defined, it is no longer possible to change what value given object represents. When you reassign an integer, you are really creating a new int
object and changing the reference to point to this new object.
Which leads us to observed behaviour: t.value
and value
are 2 separate variables – initially, they point to the same address. But when we change one of them, Python cannot change the actual value so it changes the reference. And because reference is bound to a variable, it changes for only one of them.
Using mutable types like int lets us change its content without changing the reference, so both variables still point to the same object and changes are visible for both.
Imagine you have someone’s number saved in your phone.
### In your phone:
Alice: 312 3456789
Bob: 614 7984321
Your friend Carol has some other numbers in her phone.
### In Carol's phone
Dave: 602 9406981
Erin: 216 3714732
After the dinner party, Carol asks you for Alice’s phone number, and you share it with her. Now:
### In your phone:
Alice: 312 3456789
Bob: 614 7984321
### In Carol's phone
Dave: 602 9406981
Alice: 312 3456789
Erin: 216 3714732
At some later date, Alice tells you "Hey, I’m changing phone numbers, here’s the new number" so you update your phone:
### In your phone:
Alice: 312 1111111 <--- Alice's new number
Bob: 614 7984321
### In Carol's phone
Dave: 602 9406981
Alice: 312 3456789
Erin: 216 3714732
This doesn’t magically update Carol’s phone, so she’s still got the old number. You can’t actually edit a number, it’s immutable, the thing you updated here is a phone directory – a mapping of names to numbers.
Modules in Python are analogous to this situation. Module t
has a namespace, t.__dict__
mapping names to values. Module s
also has a namespace s.__dict__
. The function t.change_value
only updates an entry for the name "value" in its own "phone directory" i.e. in t.__dict__
. There is no mechanism for that action to update other unrelated namespaces at the same time.