When is the global statement necessary? - python

I am a little confused about "global" statement.
"sample code 1" runs ok without using "global" statement; however, "sample code 2" will not run unless the "global a" is un-commented.
Why do I have to declare "global" in "sample code 2" but not in "sample code 1"?
# sample code 1
def updating():
a.append(1);
if __name__=='__main__':
a=[100];
print(f'before updating, variable a id is {id(a)}, and value is {a}')
updating()
print(f'after updating, variable a id is {id(a)}, and value is {a}')
# sample code 2
def updating():
#global a
a=a+[1]
if __name__=='__main__':
a=[100];
print(f'before updating, variable a id is {id(a)}, and value is {a}')
updating()
print(f'after updating, variable a id is {id(a)}, and value is {a}')

By default, an assignment to a name always operates on a local variable, creating a new one if it is not currently defined.
The global statement makes the name refer to a variable in the global scope, permitting you to assign to a global name without creating a new local variable.
(The nonlocal statement does something similar, except it makes the name refer to a variable defined in the closest enclosing scope, which is not necessarily the global scope.)
In your first example, you are not assigning to a name; you are performing attribute lookup on a free variable, which resolves to the global variable of the same name.
In your second example, you try to create a new local variable. Since scope is determined at compile time, a = a + [1] will fail, because the a on the right-hand side will still refer to the as-of-yet undefined local variable a. With global, the assignment does not create a local variable, so the right-hand side is an expression involving the global variable, and the result is assigned to the global name as well.

Related

get and set value in python

I want to have few global variables in my python code.
Then set their values with set function and want to get their values through get function.
For example:
a = None #global variable1
b= None #global variable2
def set(var, value):
var = value
def get(var):
return var
set(a, '1')
get(b, '2')
I want to have a generic get and set function which will do this for any global variable. How can I do this in python ? The code written here gives error.
If you're willing to pass the variable names as strings, you can do this using the globals() function. But even though you can, I don't think it's a good idea.
First, here's how you could do it:
def get_var(var_name):
return globals()[var_name]
def set_var(var_name, value):
globals()[var_name] = value
I'm not using the name set since that's a builtin type, which you probably don't want to shadow with something else.
You'd call it like:
set_var("foo", 1)
bar = get_var("foo")
print(foo, bar) # prints 1 1
So it works, but there's really no need for those functions. You can assign or fetch global variables using normal syntax instead. Just use foo = 1 instead of set_var("foo", 1) (if you're doing it in a function, put global foo first). Getting a global variable is as simple as naming it (like I do in print(foo)).
If you don't know the variable name ahead of time, you should probably be putting the name and value into a dictionary, rather than making them global variables. Variable names are for the programmer to use. They're not data!
Python is "call by value" not "call by reference". Meaning when you call "set(a, '1')", you are passing the value of "a" to the function "set". The variable "var" in "set" is a local variable to the function itself and will not effect your global variable "a". You need to give your setter and getter methods access to your global variables. You can do this by making them all part of a class object.

Python: Understanding global a little bit more

I have a program in python. part of the program is:
suggestengines = get_suggestengines(suggestengines)
sleeptimer = sleepcount * len(suggestengines)
seeds = get_seeds(dummydata=False)
For further programming I want to make a function of it:
def first_step():
suggestengines = get_suggestengines(suggestengines)
sleeptimer = sleepcount * len(suggestengines)
seeds = get_seeds(dummydata=False)
Now I get an error for "suggestengines" that I want to pass into get_suggestengines(). Also sleep timer and seeds get a marker, that I don't use them in the rest of the program. I googled it and got the answer: Us global. So I added global for everything
def first_step():
global suggestengines
global sleeptimer
global seeds
suggestengines = get_suggestengines(suggestengines) #which engines to run?
sleeptimer = sleepcount * len(suggestengines)
seeds = get_seeds(dummydata=False)
In further part of the program I have
for seed in tqdm(seeds, leave=True):
there the program gives me an error vor seeds in tqdm. If I change it to also make a def of it like:
def partTwo():
for seed in tqdm(seeds, leave=True):
Then I don't get an error anymore although I didn't used global. Can someone explain me why and if I need to use global in part 2 also?
The statement
global <identifier>
tells python that <identifier> should refer to a global when used in assignments. This is necessary in functions that change globals because Python has no syntactical difference between declaring a variable and assigning to an existing variable. The default in python is to have assignments in functions create new variables, rather than change global state.
When you just read from a variable there is no syntactic ambiguity, so Python will just use whatever variable it finds (i.e. global if there is no local one).
Example:
a = 1
def foo():
a = 2 # this will create a new, local variable a
def bar():
global a # "when I refer to a, I mean the global one"
a = 2 # this will change the global variable a
If no global with the specified name exists, the global statement itself will not create a new global variable, but any following assignment will. E.g. given the following:
def x():
global c
def y():
global c
c = 1
def z()
print c
x(); z() would be an error(global name 'c' is not defined), while y(); z() would print 1.
seeds hasn't been initialized yet by the time the for loop is hit, since its initialization is part of a def that hasn't been called yet. If you put the for loop inside a def then it will be called in the order you call the functions, so the interpreter won't complain until you actually use it.
The only thing to keep in mind here is this: use variables after they have been initialized.

Returning Variables in Functions Python Not Working Right [duplicate]

This question already has answers here:
Using global variables in a function
(25 answers)
How do I get a result (output) from a function? How can I use the result later?
(4 answers)
Closed last month.
I have been trying to return a variable in a function in a variable and use it outside of it:
test = 0
def testing():
test = 1
return test
testing()
print(test)
But when I run it, the result is 0. How could I fix this problem?
You are messing up a bit the scopes and/or assignment. Try this:
def testing():
test = 1
return test
test = testing()
print(test)
Explanation: The test inside testing is different to the test inside the module. You have to assign it on module-level to get the expected result.
Because you declare test in the function, it is not a global variable, thus, you can not access the variable test you created in the function outside of it as they are different scopes
If you want to return test to a variable, you have to do
result = testing()
print(result)
Or, you can also add a global statement:
test = 0
def testing():
global test
test = 1
return test
testing()
print(test)
By the way, when doing a conditional statement, you don't need the brackets around the 1==1 :).
TLDR: A return value must be assigned to something at the call site.
test = testing()
Functions in Python have their own scope. It is created on entering (calling) the function, and destroyed when leaving it. Assignment to a name inside a scope makes that name local to this scope - causing it to be destroyed along with the scope.
# start outer scope
test = 0 # create name outer:test
def testing():
# start inner scope
test = 1 # create name outer.testing:test
return test
# end inner scope
# destroy name outer.testing:test
testing() # new temporary inner scope outer.testing
print(test) # use name outer:test
# end outer scope
Notably, names in an inner scope may "shadow" names from an outer scope. While the name test exists in both testing and the outer scope, it does not refer to the same thing. This has two important implications:
The assignment to the inner test does not affect the outer test.
At the end of testing, the inner test is destroyed and only the outer test remains.
This is why calling testing() does not have the desired effect: it never modifies the outer test passed to print.
The return statement defines the value returned by calling a function. It does not return the name, only the value pointed to.
def testing():
test = 1 # test refers to the value 1
return test # return test => value 1
The value returned by a function is like any other value - be it from a literal, lookup, or other. Most importantly, the value does not persist unless you assign it to a name or use it directly.
testing() # call test, discard its value
test = testing() # call test, store its value as `test`
print(testing()) # call test, use its value in `print`
So in order to return something from a function for later use, you must store the result to a name. You can then use that name in a later statement. A minimal example for your case looks like this:
# we already can define testing here
# it is overwritten later on, then
def testing():
# all names we use inside of testing are gone at the end
# if we just want a value, we can skip temporary names
return 1
# store the return value of testing() for later use
test = testing()
print(test)
Addendum: It is possible for a function to modify its containing scope. However, names must then be explicitly declared as being from a foreign scope.
The nonlocal and global keywords allow to modify names from outer scopes. A nonlocal is the name in the closest matching function scope. A global is the name at the module scope, regardless of any functions in-between.
test = 0
def increment():
global test # declare test as belonging to a specific scope
test += 1
# no need to return something
# we already modified the outer `test`
print(test) # 0
increment()
print(test) # 1
Note that modifying outer names is often the sign of an anti-pattern, moreso for globals than nonlocals. Beyond small scripts, it gets difficult to trace what is accessing and modifying globals. Often, it is more appropriate to use classes or generators to hold state.
A function can always read names from its containing scope, provided it never writes to the same name. Such closures are very easy to create, and the lack of modification makes them easier to trace. Note that modifying a name anywhere in a function makes it local, unless declared global or nonlocal:
test = 0
def increment():
global test
test += 1
def show_test():
# we never modify `test`, so it is fetched from the outside
print(test)
def show_and_increment1(): # this function is broken!
print(test) # `test` is *not* the outer one, since we modify it in the function
test += 1 # modifying `test` makes it local for the *entire* function
def show_and_increment2(): # this function works!
global test # force `test` to be global
print(test)
test += 1
show_test() # 0
increment()
show_test() # 1
show_and_increment2() # 1
show_and_increment2() # 2
show_and_increment2() # 3
show_test() # 4
show_and_increment1() # UnboundLocalError: local variable 'test' referenced before assignment
Inside the function testing(), you're creating a new variable test, not referring to the one that already exists. If you want to do that, you should use a global statement in the top, as in:
def testing():
global test
...etc...
Your test variable inside the function does not have a global scope. So, if you want to store the return value in a variable and output it after that, you can do something like this:
result = testing()
print(result)

getting around immutable string python

Ok, I started coding this:
lastentry = 'first'
campdata = {'a1'=0,
'b2'=0}
class Someclass:
def on_window1_destroy(self, widget, data=None):
print campdata
def func1(self)
lastentry = 'b2'
def func2(self)
lastentry = 'a1'
def func2(self)
campdata[lastcall] +=1
But then I found out that python strings (and integers) were immutable...
So how do I get around ths?
I guess your problem is that you want to change the value of the global variable lastentry by calling func1 or func2, which doesn't work. The reason it does not work is because the variable is in the global scope, and assigning to the same name inside of a function just creates a local variable with the same name as the global one. To assign to a global variable, you need to declare it as such:
lastentry = "something"
def func1():
global lastentry #tell python to treat references to lastentry as global
lastentry = "somethingelse"
Note that you don't need to do this if all you are doing with the global value is reading it like in your third function. But if you assign to a variable, it is treated as local to its scope - which is normally the surrounding function - if you don't explicitly declare it global (or nonlocal in python3).
Global variables should only be used when neccessary as they add complexity to the code. In your case you can probably refactor your code to use an instance variable or class variable for lastentry instead of a global one.
Like others have said, there doesn't seem to be any attempt to modify a string in your code, so that's hardly the problem.
That said, lastcall looks random, should it perhaps be lastentry?
I don't see any problem with your code (except for some details). String immutability does not seem to be a problem, here.
You may want to write, instead of the code in your question:
campdata = {'a1': 0, # Not "= 0"
'b2': 0}
and
campdata[lastentry] +=1 # not "lastcall"
Also, as l4mpi mentioned, you need a global lastentry in the methods where it is modified.
Another point: relying on global variables is quite unusual. If at all possible, it would be best to use instance attributes (self.campdata, self.lastentry).

Conditionally modify global variable

I'd like to do something like this, but I get a SyntaxWarning and it doesn't work as expected
RAWR = "hi"
def test(bool):
if bool:
RAWR = "hello" # make RAWR a new variable, don't reference global in this function
else:
global RAWR
RAWR = "rawr" # reference global variable in this function
print RAWR # if bool, use local, else use global (and modify global)
How do I get this to work? Passing in True or False modifies the global variable.
You cannot. Within a scope, a specific name refers either to a local variable, or to a non-local (e.g. global, or from an outer function) variable. Not both. The global RAWR line makes RAWR a global for the entire scope (that's why you get a warning, it doesn't do what you think it does), just like assignment to a variable makes it local for the entire scope. Edit: Thanks to veredesmarald, we now know it is in fact a syntax error in Python 2. This half of my answer only applies to Python 3 apparently.
You should just use a differently-named local variable, and in the branch where you want to "promote" it to a global, set the global and the local variable. (Or just don't use globals at all.)
The only easy way you can go would be
RAWR = "hi"
def test(newone):
if newone:
lR = "hello" # make RAWR a new variable, don't reference global in this function
else:
global RAWR
lR = RAWR # reference global variable in this function
print lR # if bool, use local, else use global (and modify global)
# modify lR and then
if not newone:
RAWR = lR
Another way, however, could be to abuse the concept of classes and objects to your purposes.
class store_RAWR(object):
RAWR = "hi"
def __init__(self, new): self.RAWR = new
def test(newone):
if newone:
myR = store_RAWR("hello") # get a (temporary) object with a different string
else:
myR = store_RAWR # set the class, which is global.
# now modify myR.RAWR as you need
But this requires other program parts which use the global name to be changed as well.

Categories

Resources