Acsess from function to non-global variables [duplicate] - python

This question already has answers here:
Why isn't the 'global' keyword needed to access a global variable?
(11 answers)
Closed 6 months ago.
This is a weird behavior.
Try this :
rep_i=0
print "rep_i is" , rep_i
def test():
global rep_i #without Global this gives error but list , dict , and others don't
if rep_i==0:
print "Testing Integer %s" % rep_i
rep_i=1
return "Done"
rep_lst=[1,2,3]
def test2():
if rep_lst[0]==1:
print "Testing List %s" % rep_lst
return "Done"
if __name__=="__main__":
test()
test2()
Why list do not need to declare global? are they automatically global?
I find it really weird, I use list most of the time and I don't even use global at all to us them as global...

It isn't automatically global.
However, there's a difference between rep_i=1 and rep_lst[0]=1 - the former rebinds the name rep_i, so global is needed to prevent creation of a local slot of the same name. In the latter case, you're just modifying an existing, global object, which is found by regular name lookup (changing a list entry is like calling a member function on the list, it's not a name rebinding).
To test it out, try assigning rep_lst=[] in test2 (i.e. set it to a fresh list). Unless you declare rep_lst global, the effects won't be visible outside test2 because a local slot of the same name is created and shadows the global slot.

You only need to use global if you are assigning to the global name. Without global, an assignment creates a new local.
There's nothing special about how global applies to a list—global simply influences scope and name resolution.

There is an error in python called UnboundLocalError which often confuses newcomers. The confusing thing is: future assignment does change the way a variable is looked up.
When the interpreter sees a variable name for the first time, it looks ahead to the end of current code block, and if you don't have an assignment to it anywhere within the same block of code, the interpreter considers it global. If you do, however, then it is considered local, and any reference to it before assignment generates an UnboundLocalError. That's the error you got. That's why you need to declare global rep_i. If you did not assign rep_i, you wouldn't need this line.
Also, this has nothing to do with variable type. Also, assigning or appending an item to the list (which you probably meant to do, but did not) is not assignment of the list itself, it is essentially calling a method on a list object, which is different from assignment: assignment creates a new object (possibly under a name that already exists), while manipulating a list just changes an existing list.
You can try:
In [1]: # It won't work with small integers, as they are cached singletons in CPython
In [2]: a = 123123
In [3]: id (a)
Out[3]: 9116848
In [4]: a = 123123
In [5]: id(a)
Out[5]: 9116740
In [6]: # See, it changed
In [7]: # Now with lists
In [8]: l = [1,2,3]
In [9]: id(l)
Out[9]: 19885792
In [10]: l[1] = 2
In [11]: id(l)
Out[11]: 19885792
In [12]: # See, it is the same
In [13]: # But if i reassign the list, even to the same value
In [14]: l = [2,2,3]
In [15]: id(l)
Out[15]: 19884272

Here's an example that demonstrates that a non list/dict variable is available in a subroutine, and the problem is, as everyone says, the act of rebinding in your original code sample:
x = 1
def test():
y = x + 1
print y
test()
You'll see this prints out 2, despite x not being declared global.

If you had assigned a new value to rep_lst inside of test2 (not just to one of its elements, as you did) it would not work without the global flag. In Python, if you do not assign to a variable inside a function it will look for that variable in in more global scopes until it finds it.
For example, in this code segment I define the list both globally and inside of example(). Since the variable in example() is closer in scope to example2() than the global one is, it is what will be used.
x = ["out"]
def example():
x = ["in"]
def example2():
print x # will print ["in"]
This has nothing to do with lists, but is the behaviour of any variable in Python.

Related

Why local variables referenced before assignment?

I made the two functions below in Python 3. The first function test_list works fine with the list a without error. I can modify the list element in that function.
However, the second funciton test_int will pop an error local variable 'b' referenced before assignment. Why can't I do this to the variable b?
a=[1,2,3]
def test_list():
a[0]=2
return a
b = 2
def test_int():
b += 1
return b
b += 1 is equivalent to b = b.__iadd__(1); since the name b gets bound by that statement (l.h.s.), the compiler infers that b is a local variable; however, the r.h.s. expression contains the name b as well and thus you get the error "local variable 'b' referenced before assignment" (the local name b doesn't refer to anything at the time when the expression is evaluated).
a[0] = 2 on the other hand is equivalent to a.__setitem__(0, 2) and thus involves no name binding.
If you wish to modify a global name binding within a function, you can use the keyword global; there's also the equivalent nonlocal for names contained in an outer, but not the global, scope.
b is globally scoped. You are attempting to modify it in a local context, ie in test_int. You need the global keyword if you wish to modify a globally scoped variable in a local context. Read about variable scoping in python here.
The reason the first function test_list works is because you are not modifying the global variable itself, but only the contents. Here's a good explanation of what happens when you modify global variables: https://stackoverflow.com/a/31437415/14715054
This will fix your problem:
a=[1,2,3]
def test_list():
a[0]=2
return a
b = 2
def test_int():
global b
b += 1
return b

Saving and Loading a Class to a File [duplicate]

This question already has answers here:
Using global variables in a function
(25 answers)
Closed 5 months ago.
I know I should avoid using global variables in the first place due to confusion like this, but if I were to use them, is the following a valid way to go about using them? (I am trying to call the global copy of a variable created in a separate function.)
x = "somevalue"
def func_A ():
global x
# Do things to x
return x
def func_B():
x = func_A()
# Do things
return x
func_A()
func_B()
Does the x that the second function uses have the same value of the global copy of x that func_a uses and modifies? When calling the functions after definition, does order matter?
If you want to simply access a global variable you just use its name. However to change its value you need to use the global keyword.
E.g.
global someVar
someVar = 55
This would change the value of the global variable to 55. Otherwise it would just assign 55 to a local variable.
The order of function definition listings doesn't matter (assuming they don't refer to each other in some way), the order they are called does.
Within a Python scope, any assignment to a variable not already declared within that scope creates a new local variable unless that variable is declared earlier in the function as referring to a globally scoped variable with the keyword global.
Let's look at a modified version of your pseudocode to see what happens:
# Here, we're creating a variable 'x', in the __main__ scope.
x = 'None!'
def func_A():
# The below declaration lets the function know that we
# mean the global 'x' when we refer to that variable, not
# any local one
global x
x = 'A'
return x
def func_B():
# Here, we are somewhat mislead. We're actually involving two different
# variables named 'x'. One is local to func_B, the other is global.
# By calling func_A(), we do two things: we're reassigning the value
# of the GLOBAL x as part of func_A, and then taking that same value
# since it's returned by func_A, and assigning it to a LOCAL variable
# named 'x'.
x = func_A() # look at this as: x_local = func_A()
# Here, we're assigning the value of 'B' to the LOCAL x.
x = 'B' # look at this as: x_local = 'B'
return x # look at this as: return x_local
In fact, you could rewrite all of func_B with the variable named x_local and it would work identically.
The order matters only as far as the order in which your functions do operations that change the value of the global x. Thus in our example, order doesn't matter, since func_B calls func_A. In this example, order does matter:
def a():
global foo
foo = 'A'
def b():
global foo
foo = 'B'
b()
a()
print foo
# prints 'A' because a() was the last function to modify 'foo'.
Note that global is only required to modify global objects. You can still access them from within a function without declaring global.
Thus, we have:
x = 5
def access_only():
return x
# This returns whatever the global value of 'x' is
def modify():
global x
x = 'modified'
return x
# This function makes the global 'x' equal to 'modified', and then returns that value
def create_locally():
x = 'local!'
return x
# This function creates a new local variable named 'x', and sets it as 'local',
# and returns that. The global 'x' is untouched.
Note the difference between create_locally and access_only -- access_only is accessing the global x despite not calling global, and even though create_locally doesn't use global either, it creates a local copy since it's assigning a value.
The confusion here is why you shouldn't use global variables.
You can directly access a global variable inside a function. If you want to change the value of that global variable, use "global variable_name". See the following example:
var = 1
def global_var_change():
global var
var = "value changed"
global_var_change() #call the function for changes
print var
Generally speaking, this is not a good programming practice. By breaking namespace logic, code can become difficult to understand and debug.
As others have noted, you need to declare a variable global in a function when you want that function to be able to modify the global variable. If you only want to access it, then you don't need global.
To go into a bit more detail on that, what "modify" means is this: if you want to re-bind the global name so it points to a different object, the name must be declared global in the function.
Many operations that modify (mutate) an object do not re-bind the global name to point to a different object, and so they are all valid without declaring the name global in the function.
d = {}
l = []
o = type("object", (object,), {})()
def valid(): # these are all valid without declaring any names global!
d[0] = 1 # changes what's in d, but d still points to the same object
d[0] += 1 # ditto
d.clear() # ditto! d is now empty but it`s still the same object!
l.append(0) # l is still the same list but has an additional member
o.test = 1 # creating new attribute on o, but o is still the same object
Here is one case that caught me out, using a global as a default value of a parameter.
globVar = None # initialize value of global variable
def func(param = globVar): # use globVar as default value for param
print 'param =', param, 'globVar =', globVar # display values
def test():
global globVar
globVar = 42 # change value of global
func()
test()
=========
output: param = None, globVar = 42
I had expected param to have a value of 42. Surprise. Python 2.7 evaluated the value of globVar when it first parsed the function func. Changing the value of globVar did not affect the default value assigned to param. Delaying the evaluation, as in the following, worked as I needed it to.
def func(param = eval('globVar')): # this seems to work
print 'param =', param, 'globVar =', globVar # display values
Or, if you want to be safe,
def func(param = None)):
if param == None:
param = globVar
print 'param =', param, 'globVar =', globVar # display values
You must use the global declaration when you wish to alter the value assigned to a global variable.
You do not need it to read from a global variable. Note that calling a method on an object (even if it alters the data within that object) does not alter the value of the variable holding that object (absent reflective magic).

python nested function variable scope [duplicate]

This question already has answers here:
Short description of the scoping rules?
(9 answers)
Closed 2 years ago.
I know about nonlocal keyword and how to fix this error but I am getting a behaviour that I am not understanding in the code below. If you run this, there is no problem
def test( root):
x=2
def f2(r):
print("***")
x=r #Adding this line is ok
print("----",x)
f2(root)
return x
test(4)
Now try changing x=r to be be the last line in f2 does not work as below
def test( root):
x=2
def f2(r):
print("***")
print("----",x)
x=r #Gives an error "local variable 'x' referenced before assignment"
f2(root)
return x
test(4)
Thanks
You have two variables named 'x', in two different scopes. I would not recommend doing this; it can lead to a lot of confusion, leading to bugs in the program.
I would not suggest fixing this with keywords. Rename one of the variables. If you do, the error becomes clear.
def test( root):
x_test=2
def f2(r):
print("***")
print("----",x_f2)
x_f2=r
f2(root)
return x_test
test(4)
Clearly, x_f2 is being referenced before assignment. When you write the code like this, the error is clear.
This is exactly what your code is doing; it is just not clear because you have two variables with the same name, in different scopes.
The 'x' inside f2 is a local variable in the function, and you can not use it before assigning it. The fact that in the outer scope there is a different variable named 'x' which has been assigned, does not change that.
the problem is in line 5 where you printed x in print("----",x),
it doesnt know the x in f2 function so you cant print it
if you want to use that x=2 in f2() you should pass it to it , or using the global key or ...
In the first example, you're assigning a new local variable x that is independent of the x in the outer scope. Since you assign this value before you use it, that's fine. The x that you assign 4 to is the one that's local to f2, which is why when you return x from test it's the original 2 value.
In the second example, you do the same thing of assigning a new local variable x (which causes the outer scope's x to be shadowed, same as before), but this time you reference it before you actually do the assignment. That's what generates the error.
If you didn't assign x at all, then it wouldn't be shadowed and you would be able to print its value from the outer scope (meaning you'd see its value as 2, not 4):
>>> def test( root):
... x=2
... def f2(r):
... print("***")
... print("----",x)
... f2(root)
... return x
...
>>> test(4)
***
---- 2
2

Python global keyword [duplicate]

This question already has answers here:
Is it possible to modify a variable in python that is in an outer (enclosing), but not global, scope?
(9 answers)
Closed 5 months ago.
I am confused with the global keyword behavior in below code snippet, I was expecting 30, 30, 30 in all 3 prints.
def outer_function():
#global a ###commented intentionally
a = 20
def inner_function():
global a
a = 30
print('a =',a)
inner_function()
print('a =',a)
a = 10
outer_function()
print('a =',a)
#Output:
#30
#20 #Expecting 30 here
#30
All the confusion coming from "global a" after outer function definition. As my understanding at this point of time is " All the reference and assignment to variable become globally reflected on declaration of global keyword on that variable". If I am uncommenting that first global statement I am getting expected output 30,30,30.
Why global declaration inside inner_function and value change does not reflect on 2nd print i:e to outer_function(or outer scope), whereas got reflected in global namespace.
A common acronym to be familiar with is LEGB:
Local
Enclosed
Global
Built-in
This is the order in which Python will search the namespaces to find variable assignments.
Local
The local namespace is everything that happens within the current code block. Function definitions contain local variables that are the first thing that is found when Python looks for a variable reference. Here, Python will look in the local scope of foo first, find x with the assignment of 2 and print that. All of this happens despite x also being defined in the global namespace.
x = 1
def foo():
x = 2
print(x)
foo()
# prints:
2
When Python compiles a function, it decides whether each of the variables within the definition code block are local or global variables. Why is this important? Let's take a look at the same definition of foo, but flip the two lines inside of it. The result can be surprising
x = 1
def foo():
print(x)
x = 2
foo()
# raises:
UnboundLocalError: local variable 'x' referenced before assignment
This error occurs because Python compiles x as a local variable within foo due to the assignment of x = 2.
What you need to remember is that local variables can only access what is inside of their own scope.
Enclosed
When defining a multi-layered function, variables that are not compiled as local will search for their values in the next highest namespace. Here is a simple example.
x = 0
def outer_0():
x = 1
def outer_1():
def inner():
print(x)
inner()
outer_1()
outer_0()
# print:
1
When inner() is compiled, Python sets x as a global variable, meaning it will try to access other assignments of x outside of the local scope. The order in which Python searches for a value of x in moving upward through the enclosing namespaces. x is not contained in the namespace of outer_1, so it checks outer_0, finds a values and uses that assignment for the x within inner.
x --> inner --> outer_1 --> outer_0 [ --> global, not reached in this example]
You can force a variable to not be local using the keywords nonlocal and global (note: nonlocal is only available in Python 3). These are directives to the compiler about the variable scope.
nonlocal
Using the nonlocal keyword tells python to assign the variable to first instance found as it moves upward through the namespaces. Any changes made to the variable will be made in the variable's original namespace as well. In the example below, when 2 is assigned x, it is setting the value of x in the scope of outer_0 as well.
x = 0
def outer_0():
x = 1
def outer_1():
def inner():
nonlocal x
print('inner :', x)
x = 2
inner()
outer_1()
print('outer_0:', x)
outer_0()
# prints:
inner : 1
outer_0: 2
Global
The global namespace is the highest level namespace that you program is running in. It is also the highest enclosing namespace for all function definitions. In general it is not good practice to pass values in and out of variables in the global namespace as unexpected side effects can occur.
global
Using the global keyword is similar to non-local, but instead of moving upward through the namespace layers, it only searches in the global namespace for the variable reference. Using the same example from above, but in this case declaring global x tells Python to use the assignment of x in the global namespace. Here the global namespace has x = 0:
x = 0
def outer_0():
x = 1
def outer_1():
def inner():
global x
print('inner :', x)
inner()
outer_1()
outer_0()
# prints:
0
Similarly, if a variable is not yet defined in the global namespace, it will raise an error.
def foo():
z = 1
def bar():
global z
print(z)
bar()
foo()
# raises:
NameError: name 'z' is not defined
Built-in
Last of all, Python will check for built-in keywords. Native Python functions such as list and int are the final reference Python checks for AFTER checking for variables. You can overload native Python functions (but please don't do this, it is a bad idea).
Here is an example of something you SHOULD NOT DO. In dumb we overload the the native Python list function by assigning it to 0 in the scope of dumb. In the even_dumber, when we try to split the string into a list of letters using list, Python will find the reference to list in the enclosing namespace of dumb and try to use that, raising an error.
def dumb():
list = 0
def even_dumber():
x = list('abc')
print(x)
even_dumber()
dumb()
# raises:
TypeError: 'int' object is not callable
You can get back the original behavior by referencing the global definition of list using:
def dumb():
list = [1]
def even_dumber():
global list
x = list('abc')
print(x)
even_dumber()
dumb()
# returns:
['a', 'b', 'c']
But again, DO NOT DO THIS, it is bad coding practice.
I hope this helps bring to light some of how the namespaces work in Python. If you want more information, chapter 7 of Fluent Python by Luciano Ramalho has a wonderful in-depth walkthrough of namespaces and closures in Python.
From the documentation:
The global statement is a declaration which holds for the entire
current code block. It means that the listed identifiers are to be
interpreted as globals.
Note it only applies to current code block. So the global in inner_function only applies within inner_function. Outside of it, the identifier is not global.
Note how “identifier” is not the same as “variable”. So what it tells the interpreter is “when I use identifier a within this code block, do not apply normal scope resolution, I actually mean the module-level variable, ”.
Just uncomment your global command in the outer_function, otherwise you're declaring a local variable with value 20, changing a global variable then printing that same local variable.
It's not a good idea use global variabilities. If you want only reset the value of a variable, you just use this lines:
def outer_function():
a = 20
def inner_function():
a = 30
print('a =',a)
return a
a = inner_function()
print('a =',a)
return a
a = 10
a = outer_function()
print('a =',a)

How to make two functions share the same non global variable (Python)

Is there a way to make function B to be able to access a non global variable that was declared in only in function A, without return statements from function A.
As asked, the question:
Define two functions:
p: prints the value of a variable
q: increments the variable
such that
Initial value of the variable is 0. You can't define the variable in the global
enviroment.
Variable is not located in the global environment and the only way to change it is by invoking q().
The global enviroment should know only p() and q().
Tip: 1) In python, a function can return more than 1 value. 2) A function can be
assigned to a variable.
# Example:
>>> p()
0
>>> q()
>>> q()
>>> p()
2
The question says the global enviroment should know only p and q.
So, taking that literally, it could be done inline using a single function scope:
>>> p, q = (lambda x=[0]: (lambda: print(x[0]), lambda: x.__setitem__(0, x[0] + 1)))()
>>> p()
0
>>> q()
>>> q()
>>> p()
2
Using the tips provided as clues, it could be done something like this:
def make_p_and_q():
context = {'local_var': 0}
def p():
print('{}'.format(context['local_var']))
def q():
context['local_var'] += 1
return p, q
p, q = make_p_and_q()
p() # --> 0
q()
q()
p() # --> 2
The collection of things that functions can access is generally called its scope. One interpretation of your question is whether B can access a "local variable" of A; that is, one that is defined normally as
def A():
x = 1
The answer here is "not easily": Python lets you do a lot, but local variables are one of the things that are not meant to be accessed inside a function.
I suspect what your teacher is getting at is that A can modify things outside of its scope, in order to send information out without sending it through the return value. (Whether this is good coding practise is another matter.) For example, functions are themselves Python objects, and you can assign arbitrary properties to Python objects, so you can actually store values on the function object and read them from outside it.
def a():
a.key = "value"
a()
print a.key
Introspection and hacking with function objects
In fact, you can sort of get at the constant values defined in A by looking at the compiled Python object generated when you define a function. For example, in the example above, "value" is a constant, and constants are stored on the code object:
In [9]: a.func_code.co_consts
Out[9]: (None, 'value')
This is probably not what you meant.
Firstly, it's bad practise to do so. Such variables make debugging difficult and are easy to lose track of, especially in complex code.
Having said that, you can accomplish what you want by declaring a variable as global:
def funcA():
global foo
foo = 3
def funcB():
print foo # output is 3
That's one weird homework assignment; especially the tips make me suspect that you've misunderstood or left out something.
Anyway, here's a simpler solution than the accepted answer: Since calls to q increment the value of the variable, it must be a persistent ("static") variable of some sort. Store it somewhere other than the global namespace, and tell p about it. The obvious place to store it is as an attribute of q:
def q():
q.x += 1
q.x = 0 # Initialize
def p():
print(q.x)

Categories