Global variables in a Python module spontaneously reset
<p>I have part of a program written in Python 3.5 and started by testing the first two modules. I managed to isolate a problem in one of the modules where it appears that two global variables are switching back to their original values for no reason that I can understand. One of these global variables (<code>event_count</code>) is used in only a single function (grep shows the string "event_count" doesn't appear anywhere else in any of my *.py files), yet the value of the variable changes between calls to the function. If I add print statements for the other global variable in this module, it also reverts to it's original value at the same moment. Moving <code>event_count</code> to another module (replacing it with <code>sensorlogic.event_count</code> in <code>eventcount()</code> and moving the initialization to the other module) makes the behavior go away, so I have a fix but no understanding.</p>

<p>Here is all of the code that uses <code>event_count</code>, in module <code>sensoreval</code>:</p>

<pre><code>event_count = 0

def eventcount(increment):
    global event_count
    print("entering eventcount, increment =", increment,
          ", event_count =", event_count)
    event_count += increment
    print("leaving eventcount, event_count =", event_count)
    return event_count
</code></pre>

<p>If I run the following code segment:</p>

<pre><code>    e.setvalue(1)
    print("I am at marker #1")
    eventcount(0)
</code></pre>

<p>(the last action in <code>e.setvalue()</code> is a call to <code>eventcount(0)</code>) it produces this output:</p>

<pre><code>entering eventcount, increment = 0 , event_count = 4
leaving eventcount, event_count = 4
I am at marker #1
entering eventcount, increment = 0 , event_count = 0
leaving eventcount, event_count = 0
</code></pre>

<p>I have tried trimming down the two modules to something of reasonable size, but the problem keeps going away when I do so. I'll keep working on that. Since I've never used Python 3 before, and only have a little Python 2.7 experience I assume I'm doing something stupid, I just have no idea what.</p>

<p>I believe that my example is different from some of the related posts that have been pointed out in that the variable <code>event_count</code> is global only so it will be static. It is used only in this single function. The string "event_count" doesn't appear anywhere else in this or any other module.</p>

<hr>

<p>After many edit/rerun iterations, I have a managably small example that demonstrates what is happening. It involves two modules with a total of 8 lines of code. The first module, <code>a.py</code>, is <code>__main__</code>:</p>

<pre><code>import b
c = 0
if __name__ == '__main__': 
    b.init()
    print("c =", c)
</code></pre>

<p>The second module is <code>b.py</code>:</p>

<pre><code>import a
def init():
    a.c = 1
</code></pre>

<p>Running <code>a.py</code> produces the output:</p>

<pre><code>c = 0
</code></pre>

<p>I expected <code>c</code> to still be 1 from the <code>a.c = 1</code> in <code>b.py</code>.</p>

<p>Also, I tried to reduce this further by deleting the <code>if __name__ == '__main__'</code> from <code>a.py</code>, but then the example no longer runs:</p>

<pre><code>Traceback (most recent call last):
  File "...\a.py", line 1, in &lt;module&gt;
    import b
  File "...\b.py", line 1, in &lt;module&gt;
    import a
  File "...\a.py", line 3, in &lt;module&gt;
    b.init()
AttributeError: module 'b' has no attribute 'init'
</code></pre>

<p>I can't explain that, either, but it seems likely to be related.</p>

<hr>

<p>Following Mata's lead, I believe that the following code shows what's going on. There are three modules involved. <code>a.py</code>:</p>

<pre><code>print("__name__ =", __name__)
import b
print("__name__ =", __name__)
def f(): pass
print(f)
if __name__ == '__main__':
    print("f is b.a.f?", f is b.a.f)
</code></pre>

<p><code>b.py</code>:</p>

<pre><code>import a
</code></pre>

<p><code>c.py</code>:</p>

<pre><code>import a
import b
print("__name__ =", __name__)
print("a.f is b.a.f?", a.f is b.a.f)
</code></pre>

<p>You can see the problem by running <code>a.py</code>, giving the result:</p>

<pre><code>__name__ = __main__
__name__ = a
__name__ = a
&lt;function f at 0x0000021A4A947840&gt;
__name__ = __main__
&lt;function f at 0x0000021A484E0400&gt;
f is b.a.f? False
</code></pre>

<p>Running <code>c.py</code> so that <code>__main__</code> isn't part of the import cycle results in:</p>

<pre><code>__name__ = a
__name__ = a
&lt;function f at 0x000001EA101B7840&gt;
__name__ = __main__
a.f is b.a.f? True
</code></pre>

<p>Let's take a look at your two-module example step-by-step. The behavior there is expected, but initially confusing and probably explains what is going on pretty well in the other cases.</p>

<p>If you run <code>a</code> as a script, it is not imported as <code>a</code> into <code>sys.modules</code>, but rather as <code>__main__</code>. The first statement is <code>import b</code>, which creates an empty module object <code>sys.modules['b']</code> and begins initializing it.</p>

<p>The first line of <code>b</code> imports <code>a</code> again. Normally, a module object under <code>sys.modules['a']</code> would be found, but in this case, you are running <code>a</code> as a script, so the initial import happened under a different name. Since the name of <code>a</code> is <code>a</code> and not <code>__main__</code> this time, <code>a.c</code> is set to zero and nothing else happens.</p>

<p>Now execution returns to <code>b</code>. It now creates a function <code>init</code>, which sets <code>sys.modules['a'].c</code> to one. I wrote out the reference to the <code>a</code> module very explicitly because this is the root cause of your discrepancy.</p>

<p>Once <code>b</code> is imported, execution returns back to <code>a</code>, but not <code>sys.modules['a']</code>. The next line, <code>c = 0</code> actually sets <code>sys.modules['__main__'].c</code> to zero. Hopefully you see the problem at this point. The next line calls <code>b.init</code>, which sets <code>sys.modules['a']</code> to one. Then you print <code>sys.modules['__main__']</code>, which is zero, as expected.</p>

<p>To verify the correctness of this exposition, try adding a print statement</p>

<pre><code>print(sys.modules['a'].c)
</code></pre>

<p>You will get <code>1</code>. Also, <code>sys.modules['a'] is sys.modules['__main__']</code> will be <code>False</code>. The easiest way to get around this is not to initialize members of other modules in a given module's import.</p>

<p>Your specific case is documented here: <a href="http://effbot.org/zone/import-confusion.htm#using-modules-as-scripts" rel="nofollow">http://effbot.org/zone/import-confusion.htm#using-modules-as-scripts</a>.</p>

<p><strong>Additional Resources</strong></p>

<p>You can get a lot more information on the gritty details of the import system here: <a href="https://docs.python.org/3/reference/import.html" rel="nofollow">https://docs.python.org/3/reference/import.html</a>. Various traps and caveats of imports are described here: <a href="http://python-notes.curiousefficiency.org/en/latest/python_concepts/import_traps.html" rel="nofollow">http://python-notes.curiousefficiency.org/en/latest/python_concepts/import_traps.html</a>.</p>

python python-3.x