Python: modify global var from a function argument

Question:

I’d like to create a function that will modify an initialized global variable based on the argument passed to it, but I get a SyntaxError: name 'arg' is local and global. I have seen other methods to accomplish this, using globals() or creating a simple func inside myFunc to “trick” Python. Another approach would be to create if statements inside myFunc to explicitly assign the corresponding global variables, but that seems overly verbose.

Why does this occur, and what would be the most efficient/elegant/Pythonic way to accomplish this?

Given:

var1 = 1
var2 = 2
var3 = 3

def myFunc(arg):
    global arg
    arg = 10

myFunc(var1) # each of these should print to 10
myFunc(var2)
myFunc(var3)
Asked By: Chrispy

||

Answers:

You can use globals() to access the variables and assign new values from within myFunc()

var1 = 1
var2 = 2

def myFunc(varname):
    globals()[varname] = 10

print(var1, var2)

myFunc("var1")
myFunc("var2")

print(var1, var2)

Will output:

1, 2
10, 10
Answered By: Holy Mackerel

In python a variable is a name for an object. When you call a function, and pass it an argument you’re passing the object associated with the variable, not the name. So for example when you call wash(mydog), you’re saying “wash the object known as mydog”. Keep in mind, that the same object could have more than one name, for example spot = mydog = best_dog_ever = new_dog(). The function doesn’t know which name was used to pass it the object, and even if it did, what if the name used was not the one in the global scope, you’d have to have some way of saying this function only takes global variables as arguments.

I hope that helps explain why you’re getting a syntax error, but you can still accomplish pretty much the same thing at least two ways. The first is to simply assign the return value to the variable you’re trying to change, for example:

var1 = 1
var2 = 2

def addone(a):
    return a + 1

def main():
    global var1, var2
    var1 = addone(var1)
    var2 = addone(var2)

print var1, var2
main()
print var1, var2

The second is to use a more object oriented approach, something like this:

class GlobalValue(object):

    def __init__(self, value):
        self.value = value

var1 = GlobalValue(1)
var2 = GlobalValue(2)

def addone(a):
    a.value += 1

print var1.value, var2.value
addone(var1)
addone(var2)
print var1.value, var2.value

Or even better:

class GlobalValue(object):

    def __init__(self, value):
        self.value = value

    def addone(self):
        self.value += 1

var1 = GlobalValue(1)
var2 = GlobalValue(2)

print var1.value, var2.value
var1.addone()
var2.addone()
print var1.value, var2.value
Answered By: Bi Rico

Why does this occur

Because the global variable that you want to use has the same name as the parameter, arg. In Python, parameters are local variables, and a variable can only be local or global, not both.

It appears as though you expected to be able to use the contents of var to, somehow, specify which existing global variable to modify. It does not work like that. First off, variables don’t contain other variables; they contain values. The name of a variable isn’t a value. Again, parameters are local variables – and calling a function assigns to those variables. It assigns a value. (Keep in mind that you could just as easily call the function without a variable for the argument: myFunc(3). Should this cause 3 to become equal to 10 somehow?)

Second, even if you passed, for example, a string, you would have to do more work in order to access the corresponding global variable. It can be done, but please do not.

and what would be the most efficient/elegant/Pythonic way to accomplish this?

The Pythonic way is don’t. If you want your function to communicate information out when it is called, return a value:

def myFunc():
    return 10

var1 = myFunc()
var2 = myFunc()
var3 = myFunc()

The simplest way to fix the error is to just rename the global variable. However, this does not fix the apparent intent of the code:

var1 = 1
var2 = 2
var3 = 3

def myFunc(arg):
    global g
    g = 10

# var1, var2 and var3 will NOT CHANGE
# Instead, the new global variable g is created, and assigned a value of 10,
# three times in a row.
myFunc(var1)
myFunc(var2)
myFunc(var3)
Answered By: Karl Knechtel