0

All of the following is in one file:

def inner_foo(in_arg):
    global x;
    x += 99
    return in_arg + 1

def outer_foo(in_arg):
    global x;
    x = 0
    output = inner_foo(in_arg)
    return output    

result = outer_foo(5)
print("result == ", result)
print("x == ", x)

It works just fine when it is all in one file. Here is what gets printed:

result ==  6
x ==  99

However, if we try to split the program across multiple files, we run into problems.

# CONTENTS OF inner_foo.py
def inner_foo(in_arg):
global x;
x += 99
return in_arg + 1

Here is the other file:

# CONTENTS OF outer_foo.py
from inner_foo import inner_foo
def outer_foo(in_arg):
    global x
    x = 0
    output = inner_foo(in_arg)
    return output

result = outer_foo(5)
print("result == ", result)
print("x == ", x)

We get an error NameError: name 'x' is not defined on the line x += 99 inside inner_foo.py

Changing the import statement to include x (from inner_foo import inner_foo, x) gives us:

ImportError: cannot import name 'x'
Toothpick Anemone
  • 4,290
  • 2
  • 20
  • 42

1 Answers1

1

Python "global" scope is actually module-level, as you inferred when you tried to import. The problem is, when you do x += 1, that is the equivalent to x = x + 1, but x has never been defined in your module. So, suppose we have a.py:

def f():
    global x
    x += 1

There is no x in a, there is only f. So now, let's open up a REPL:

>>> import a
>>> a.f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/juan/workspace/temp/globals_modules/a.py", line 3, in f
    x += 1
NameError: name 'x' is not defined

And, as stated above, "global scope" is actually module-level scope, so f's global scope is module a's namespace, not necessarily the caller's global scope, i.e.:

>>> x = 0
>>> a.f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/juan/workspace/temp/globals_modules/a.py", line 3, in f
    x += 1
NameError: name 'x' is not defined

So, actually, it is looking for x in a's namespace, so if we do the following:

>>> a.x = 0
>>> a.f()
>>> a.x
1
>>> a.f()
>>> a.x
2

It works! So to put it as succinctly as possible:

>>> a.f.__globals__ is globals()
False

However!

Now that you are armed with this knowledge, do not go down this design path. Relying on changing global state across modules is a path to sorrow. Don't do it, it isn't recommended because generations of coders have found this is bad. Redesign your approach, and you will build more robust, easier to reason-about, and less buggy software.

juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172