Python: modify global var from a function argument [duplicate] - python

This question already has answers here:
How do I create variable variables?
(17 answers)
How do I get a result (output) from a function? How can I use the result later?
(4 answers)
Closed 7 months ago.
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)

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

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

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)

Related

What are the meanings and differences between "uses" and "rebinds" a variable?

From Python in a Nutshell:
Eschew global
Never use global if the function body just uses a global
variable (including mutating the object bound to that
variable, when the object is mutable).
Use a global statement only if
the function body rebinds a global variable (generally by assigning to the variable’s name).
What are the meanings and differences between "uses" and "rebinds" a variable?
Is "mutating the object bound to a variable" "uses" or "rebinds" the variable? Why?
"Mutate" and "bind"/"rebind" are two mutually exclusive operations. Mutating changes an object, whereas binding changes a name.
This is binding:
a = []
This is mutating:
a.append(None)
"Using" means accessing the existing object bound to a name, whether for reading or for mutation.
Using a variable
When you use a variable, you are using the actually value of the variable - the object to which it refers - or mutating the object that the variable name refers to. Here is an example:
>>> var1 = 1
>>> var2 = [1]
>>>
>>> def func():
print(var1)
var2.append(2)
>>> func()
1
>>> var2
[1, 2]
>>>
In the above example, we are using var1 and var2 inside of func. We use var1 because we use it's value in our call to print. And we used var2 because we mutated the object it referred to. Notice we didn't change the object to which var2 referred, we used the already existing object and modified it. Also notice that we never tried to assign a new value to either variable.
Rebinding a variable
When you rebind a variable, you are changing the object to which the variable name refers. Here is another example to help illustrate the point:
>>> var1 = 1
>>>
>>> def func():
global var1
var1 = 2
>>> func()
>>> var1
2
>>>
In the above examples. We are rebinding var inside of func. var1 use to have a reference to the object 1, but because we rebinding var1 to 2, it now refers to the object 2.
So what's the difference?
The difference is that when we use a variable, we are simply using the object to which the variable already refers. When we rebind a variable, we change the object to which the variable refers.

Python Creating Variable Names from Strings and other Variables [duplicate]

This question already has answers here:
How do I create variable variables?
(17 answers)
Closed 5 years ago.
I am currently writing my first script in python and have functions that are all similar, except for the fact that one number in each variables name changes.
For example like this:
def Function1():
global Var1
Var1 = A1
global Var2
Var2 = B1
def Function2():
global Var1
Var1 = A2
global Var2
Var2 = B2
The variables Ax, Bx and so on are read from a file. Then, if I click a tkinter-Button, one of these functions is activated and the global variables Var1 and Var2 are set to either A1 and B1 or A2 and B2 and so on...
Since this is just copy and pasting the functions and then manually changing the number in the variable name that is read from the file, I thought there has to be a way to make this easier. Something like this:
def Function1():
FUNCTIONNUMBER = 1
global Var1
Var1 = ("A" + FUNCTIONNUMBER)
global Var2
Var2 = ("B" + FUNCTIONNUMEBR)
In other words, something that makes it possible to set up a variable name from a constant part ("A") and a changing part ("1" or "2" or "3"...)
I´ve already searched for this question, but didn´t find anything that helps me with my specific case, since I don´t want to create variables out of nowhere or I don´t want to write all the variable names in a dictionary. I just want to make python think it reads a variable name that consists of different parts.
Thank you in advance!
I think what you're looking for is a list. let me show you and example
some_list = ["hey", "hows", "it", "going"]
print(some_list[0])
print(some_list[2])
This output would be:
hey
it
a list is just that, a list of numbers/strings/function/other lists, and you access them by specifying which position in the list you want (starting at 0)

Order of execution in Python methods [duplicate]

This question already has answers here:
Assigning to variable from parent function: "Local variable referenced before assignment" [duplicate]
(5 answers)
Closed 9 years ago.
I've tried looking at a few different examples, but I'm not really sure why this isn't working. Say I've some code like this:
def loadVariable():
global count
count = 0
def loadDictionary():
location = 'some location'
global myDict
myDict = pickle.load(open(location, 'rb'))
def main():
loadVariable()
loadDictionary()
for item in myDict:
if item.startswith("rt"):
count += 1
item = item[3:]
if __name__ == '__main__':
main()
To my eyes, the if statement is executed which starts the main() method. Then, the variable which is global is loaded, the dictionary is loaded and the for loop is executed.
However, when I run the code I am told that the local variable count is referenced before its assignment. Why is that happening?
Edit (Explaining some of the things I've written in comments):
This doesn't work (although I think that's because global is used wrong here):
global count
def loadVariables()
count = 0
def main():
loadVariables()
rest of code etc
This doesn't work either:
def loadVariables()
count = 0
def main():
global count
loadVariables()
rest of code etc
The only way thus far I've gotten it to work is using the link provided above, which is to treat the count as a list, like so:
def loadVariables():
global count
count = [0]
def main():
loadVariables():
rest of code etc
count[0] += 1
global means that within the function containing the global declaration, the name in the global declaration refers to a global variable. It does not mean "this thing is a global variable; treat it as global everywhere." In main, the names count and myDict refer to local variables, because main does not declare that it wants to use the globals.
The issue is that you're not declaring count as a global variable in the main function, so when the compiler sees that you're (eventually) assigning to it, it assumes that it's a local variable. Since it's value is read before it's assigned, you get an exception.
So, the most basic fix is just to add global count at the top of main(), but I think avoiding globals would be a better option. Why not have loadVariable and loadDictionary return their results, rather than assigning them to globals? If in main() you did count = loadVariable(), count would be a local variable, and you'd have no problems later trying to reassign it.
Here's a simple example of how global works
global_var = 0
def updater():
global global_var
global_var += 1
def stuff(x):
updater()
return global_var + x
if __name__ == '__main__':
stuff(2) # returns 3

Do you change variables AFTER you run a function in python?

So I wrote this function from a book I am reading, and this is how it starts:
def cheese_and_crackers(cheese_count, boxes_of_crackers):
print "You have %d cheeses!" % cheese_count
print "You have %d boxes of crackers!" % boxes_of_crackers
print "Man that's enough for a party!"
print "Get a blanket.\n"
ok, makes sense. and then, this is when this function is run where I got a little confused and wanted to confirm something:
print "OR, we can use variables from our script:"
amount_of_cheese = 10
amount_of_crackers = 50
cheese_and_crackers(amount_of_cheese, amount_of_crackers)
the thing that confused me here is that the amount_of_cheese and amount_of_crackers is changing the variables (verbage? not sure if i am saying the right lingo) from cheese_count and boxes_of_crackers repectively from the first inital variable labels in the function.
so my question is, when you are using a different variable from the one that is used in the initial function you wrote, why would you change the name of the AFTER you wrote out the new variable names? how would the program know what the new variables are if it is shown after it?
i thought python reads programs top to bottom, or does it do it bottom to top?
does that make sense? i'm not sure how to explain it. thank you for any help. :)
(python 2.7)
I think you are just a bit confused on the naming rules for parameter passing.
Consider:
def foo(a, b):
print a
print b
and you can call foo as follows:
x = 1
y = 2
foo(x, y)
and you'll see:
1
2
The variable names of the arguments (a, b) in the function signature (1st line of function definition) do not have to agree with the actual variable names used when you invoke the function.
Think of it as this, when you call:
foo(x, y)
It's saying: "invoke the function foo; pass x in as a, pass y in as b". Furthermore, the arguments here are passed in as copies, so if you were to modify them inside the function, it won't change the values outside of the function, from where it was invoked. Consider the following:
def bar(a, b):
a = a + 1
b = b + 2
print a
x = 0
y = 0
bar(x, y)
print x
print y
and you'll see:
1
2
0
0
The script runs from top to bottom. The function executes when you call it, not when you define it.
I'd suggest trying to understand concepts like variables and function argument passing first.
def change(variable):
print variable
var1 = 1
change(var1)
In the above example, var1 is a variable in the main thread of execution.
When you call a function like change(), the scope changes. Variables you declared outside that function cease to exist so long as you're still in the function's scope. However, if you pass it an argument, such as var1, then you can use that value inside your function, by the name you give it in the function declaration: in this case, variable. But it is entirely separate from var! The value is the same, but it is a different variable!
Your question relates to function parameter transfer.
There are two types of parameter transfer into a function:
By value ------- value changed in function domain but not global domain
By reference ------- value changed in global domain
In python, non-atomic types are transferred by reference; atomic types (like string, integer) is transferred by value.
For example,
Case 1:
x = 20
def foo(x):
x+=10
foo()
print x // 20, rather than 30
Case 2:
d = {}
def foo(x): x['key']=20
foo(d)
print d // {'key': 20}

Python and closed variables [duplicate]

This question already has answers here:
nonlocal keyword in Python 2.x
(10 answers)
Closed 6 months ago.
Have a look at this code:
def closure():
value = False
def method_1():
value = True
def method_2():
print 'value is:', value
method_1()
method_2()
closure()
I would expect it to print 'Value is: True' but it doesn't. Why is this and what is the solution?
This happens because method_1 gets its own local scope where it can declare variables. Python sees value = True and thinks you're creating a new variable named value, local to method_1.
The reason Python does this is to avoid polluting the outer scope's locals with variables from an inner function. (You wouldn't want assignments in regular, module-level functions to result in global variables being created!)
If you don't assign to value, then Python searches the outer scopes looking for the variable (so reading the variable works as expected, as demonstrated by your method_2).
One way to get around this is by using a mutable object instead of assigment:
result = { 'value': False }
def method_1():
result['value'] = True
In Python 3, the nonlocal statement (see also docs) was added for exactly this scenario:
def method_1():
nonlocal value
value = True # Works as expected -- assigns to `value` from outer scope
In method_1, Python assumes (quite sensibly!) that value is a local variable. Whenever you assign to a variable name inside a function, it is assumed that that variable name is a new local variable. If you want it to be global, then you have to declare it as global, and if you want it to be "nonlocal", in Python 3, you can declare it nonlocal, but in Python 2, you have to do something uglier: store the value in a container. That avoids having to reassign the variable name, and so avoids the scoping ambiguity.
def method_1_global():
global value
value = True
def method_1_nonlocal_P3():
nonlocal value
value = True
value = [False]
def method_1_nonlocal_P2():
value[0] = True
When you assign to a variable, it assumes the variable is of local scope. So the value in method_1 is not the value in closure.
If you want this to work on Python 3, add a line to method_1: nonlocal value.
On Python 2,
def closure():
value = [False]
def method_1():
value[0] = True
def method_2():
print 'value is:', value
method_1()
method_2()
closure()
is one possible work-around.
This is because you are not actually modified the closed-over variable - you are masking it with a new one that has the same name. In python 2.x there is no easy way around this, which is why the nonlocal keyword was added in python 3.
This can be worked around, however, using mutable types like list and dictionary. There is a good example in this answer.
To avoid this, you can use a list.
value = [False]
def method_1():
value[0] = True
What Python does now is searching in higher levels of the scope as value is not available in the local variables. As value is a list and Python refernces it as a global variable relative to *method_1*, you can treat value as it is, a list.

Categories

Resources