Search code examples
pythonpython-3.xpython-2.7scopeatexit

In a Python atexit function, strings set inside a function aren't visible?


I ran into (what seems to me like) an unusual scope problem with atexit under Python 2.7.10 yesterday (though the problem is also in Python 3). A global string set inside a function seems not to be visible to my atexit function, whereas global lists are fine. I've reduced the problem case to this:

import atexit

myString1 = None
myString2 = None
myArray1 = []
myArray2 = []

def setString1(newString):
    myString1 = newString

def setArray1(newString):
    myArray1.append(newString)

def testFinishUp():
    print("myString1 = " + str(myString1))
    print("myString2 = " + str(myString2))
    print("myArray1  = " + str(myArray1))
    print("myArray2  = " + str(myArray2))

setString1("Hello")
myString2 = "World"
setArray1("HELLO")
myArray2.append("WORLD")
atexit.register(testFinishUp)

What this outputs is:

myString1 = None
myString2 = World
myArray1  = ['HELLO']
myArray2  = ['WORLD']

Now, I'm sure that there's a logical scope-related reason why myString1 isn't behaving in the same way as all the others, but I for one can't see what it is. :-(

Can anyone please explain what is going on here? Thanks!


Solution

  • There's nothing strange about this. Your problem is that this statement:

    A global string set inside a function seems not to be visible to my atexit function, whereas global lists are fine.

    is false. You are not setting the global string inside the function. Instead, you create a local version of it inside the function scope that shadows the global one. If you actually want to change the global value, you should do this:

    def setString1(newString):
        global myString1
        myString1 = newString
    

    The reason that it works for your lists is that you modify them in-place (via append) instead of creating a copy. If you would instead do

    def setArray1(newString):
        myArray1 = myArray1 + [newString]
    

    you would see the exact same behavior as in the string case.

    A word of warning though: Modifying global state from functions is often considered bad style, since it obfuscates where values are set. Usually, it is better to modify the values in global scope, e.g. by assigning to the result of a function call.