5

I cannot modify global variables interactively in Spyder 4.0.0 with Python 3.8, Windows 10. There must have been a recent change because this was possible before.

I have the following example file:

x = 5
def IncreaseX():
    global x
    x += 1

print(x)
IncreaseX()
print(x)
  1. When I run it (with F5), I get the desired output:
In [1]: runfile('TestGlobals.py', wdir='D:')
5
6
  1. However, if I try to run the IncreaseX() function from the embedded Ipython console, it does not change the value of the global variable:
In [2]: x
Out[2]: 6

In [3]: IncreaseX()

In [4]: x
Out[4]: 6
  1. The same occurs if I select the last 3 rows of my example, and I run them (with F9):
In [5]: print(x)
   ...: IncreaseX()
   ...: print(x)
6
6
  1. The behaviour is different if I select all the rows of my example file and run them (with F9):
In [6]: x = 5
   ...: def IncreaseX():
   ...:     global x
   ...:     x += 1
   ...: 
   ...: print(x)
   ...: IncreaseX()
   ...: print(x)
5
6
  1. After I do this, I can modify the value of the global variable. Repeating the exact same code of step 2., I obtain the desired outcome:
In [7]: x
Out[7]: 6

In [8]: IncreaseX()

In [9]: x
Out[9]: 7

Does anyone understand this behaviour? How can I restore the old behaviour (i.e., having step 2. to work directly as step 5.)?

I know that it is generally very bad to use global variables. However, I need to interactively modify some variables while I am controlling an experiment, hence a few correctly working global variables strongly simplify my workflow.


FOUND IT!!

Spyder/Preferences/Run/General settings/Run in console's namespace instead of an empty one

With this option turned on, the old behaviour of globals is restored.

Dollarius
  • 71
  • 1
  • 4
  • (https://stackoverflow.com/questions/10588317/python-function-global-variables) Will this be useful? – Kartheek Dec 09 '19 at 10:57
  • (*Spyder maintainer here*) Sorry for the confusion, but we changed our default evaluation mode because it's safer for beginners and avoids problems with reproducibility by not depending on previous results. – Carlos Cordoba Dec 09 '19 at 16:01
  • @CarlosCordoba I understand your point, but I am afraid that many users could find themselves with broken code. One of the nice things of Spyder (also similar to Matlab) is that variables can be printed/changed dinamycally from the console: this is very convenient for scientific purposes, like reading a measurement instrument/plotting data. You may want to consider to clearly explain this change in a popup upon first starting Spyder 4. Thanks for your (and the other developers') excellent work! – Dollarius Dec 10 '19 at 05:33
  • Unfortunately people don't read popups, but thanks for the advice. We'll see what happens. Besides, variables can be modified in the console after code is executed in this new version. The thing is they won't be picked up next time code is rerun unless you move your changes to the editor. – Carlos Cordoba Dec 10 '19 at 12:51

1 Answers1

1

Looks like you're confused with Python's (quite idiosyncratic) definition of what a "global" variable is -and you're not the only one actually, as it's rather different from the usual definition xD

In most languages, "global" means "process global" - a variable that is shared by all the modules / functions / whatever used by the current process.

Python does not have true "process global" scope and "global" actually means "module level" - each module is it's own namespace, and a global in a module is actually only "global" within this module. In the code using your module, you have to refer to this variable as "yourmodulename.x".

A short example may help...

mymodule.py:

x = 1

def inc():
   global x # within this module, this refers to `mymodule.x`, not any other 'x'
   x += 1

def show():
   # no need for the global kw if it's only for reading
   print("in mymodule, x = {}".format(x))

working.py:

import mymodule

print("mymodule.x before: {}".format(mymodule.x))
mymodule.show()
mymodule.inc()
print("mymodule.x after: {}".format(mymodule.x))
mymodule.show()

broken1.py:

import mymodule

# this 'x' belongs to broken1, and `mymodule` isn't even
# aware it exists... 
x = 42 
print("broken1.x before: {}".format(x))
mymodule.show() # setting broken1.x to 42 didn't change mymodule
mymodule.inc()
# our own 'x' has not changed
print("broken1.x after: {}".format(x))
# but mymodule.x did
mymodule.show()

Note that importing x from mymodule will not work as expected either - it only creates a new x in the importing module with the initial value of mymodule.x, but those names are STILL totally unrelated, and rebinding one will not rebind the other:

broken2.py:

from mymodule import x 
from mymodule import inc, show

print("broken2.x before: {}".format(x))
show() # so far we have the same values, BUT:
inc() # this rebinds mymodule.x but has no impact here
# our own 'x' has not changed
print("broken2.x after: {}".format(x))
# but mymodule.x did
show()

For more explanations on what Python's "variables" really are and why they behave that way, you definitly want to read Ned Batchelder's reference article here

So the solution to your problem is quite simply to always use mymodule.x (outside of mymodule of course):

working2.py:

import mymodule

mymodule.show()
mymodule.inc()
mymodule.show()

mymodule.x = 666
mymodule.show() # gives the expected result
# let's go to 667
mymodule.inc()
mymodule.show()
bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118
  • Thanks a lot for your detailed reply! I was unaware of this "module global" behaviour. However, this was not my problem since I am not running any import. I found the desired option in Spyder [I updated my post above], they must have added it only recently. Thanks again for your explanation! – Dollarius Dec 09 '19 at 13:41