Python closure function losing outer variable access - python

I just learned python # decorator, it's cool, but soon I found my modified code coming out weird problems.
def with_wrapper(param1):
def dummy_wrapper(fn):
print param1
param1 = 'new'
fn(param1)
return dummy_wrapper
def dummy():
#with_wrapper('param1')
def implementation(param2):
print param2
dummy()
I debug it, it throws out exception at print param1
UnboundLocalError: local variable 'param1' referenced before assignment
If I remove param1 = 'new' this line, without any modify operation (link to new object) on variables from outer scope, this routine might working.
Is it meaning I only have made one copy of outer scope variables, then make modification?
Thanks Delnan, it's essential to closure.
Likely answer from here:
What limitations have closures in Python compared to language X closures?
Similar code as:
def e(a):
def f():
print a
a = '1'
f()
e('2')
And also this seems previous annoying global variable:
a = '1'
def b():
#global a
print a
a = '2'
b()
This is fixed by add global symbol.
But for closure, no such symbol found.
Thanks unutbu, Python 3 gave us nonlocal.
I know from above directly accessing to outer variable is read-only.
but it's kind of uncomfortable to see preceded reading variable(print var) is also affected.

When Python parses a function, it notes whenever it finds a variable used on the left-hand side of an assignment, such as
param1 = 'new'
It assumes that all such variables are local to the function.
So when you precede this assignment with
print param1
an error occurs because Python does not have a value for this local variable at the time the print statement is executed.
In Python3 you can fix this by declaring that param1 is nonlocal:
def with_wrapper(param1):
def dummy_wrapper(fn):
nonlocal param1
print param1
param1 = 'new'
fn(param1)
return dummy_wrapper
In Python2 you have to resort to a trick, such as passing param1 inside a list (or some other mutable object):
def with_wrapper(param1_list):
def dummy_wrapper(fn):
print param1_list[0]
param1_list[0] = 'new' # mutate the value inside the list
fn(param1_list[0])
return dummy_wrapper
def dummy():
#with_wrapper(['param1']) # <--- Note we pass a list here
def implementation(param2):
print param2

You assign param1 in the function, which makes param1 a local variable. However, it hasn't been assigned at the point you're printing it, so you get an error. Python doesn't fall back to looking for variables in outer scopes.

Handy for temporary situations or exploratory code (but otherwise not good practice):
If you're wanting to capture a variable from the outer scope in Python 2.x then using global is also an option (with the usual provisos).
While the following will throw (assignment of outer1 within inner makes it local and hence unbounded in the if condition):
def outer():
outer1 = 1
def inner():
if outer1 == 1:
outer1 = 2
print('attempted to accessed outer %d' % outer1)
This will not:
def outer():
global outer1
outer1 = 1
def inner():
global outer1
if outer1 == 1:
outer1 = 2
print('accessed outer %d' % outer1)

Related

how to return a global variable for access in different functions [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).

Why can I edit object by calling their methods but not referencing their variable names from within a smaller scope

My main() function is:
def main():
...
def _help():
...
1 a += somelist
2 a.append(something)
a=[]
_help()
What's weird is that line 2 works perfectly fine, but line 1 throws an UnboundLocalError: Local variable 'a' referenced before assignment.
Even when I declare a as a global variable at the top of either main or _help, it still doesn't work. Why is this?
Both of these lines are editing the same variable which makes me think either both or neither of them should work. How do I get line 1 to work?
Whenever you use <variable> = <something> in Python, Python automatically assumes it is a local variable, unless specifically told otherwise.
For example:
a = 1
def f():
if False:
a = 0
print(a) # UnboundLocalError
f()
In this case, += works as assignment as well, but .append does not assign to a, but calls a method.
This is fixed by placing a nonlocal a in your function, so it can assign to the a outside of its scope:
def main():
...
def _help():
nonlocal a
a += somelist # Works!
But in this case, you can just do a.extend(somelist).

Why Python allows reading global variables in functions without declaring them as such? [duplicate]

This question already has answers here:
UnboundLocalError trying to use a variable (supposed to be global) that is (re)assigned (even after first use)
(14 answers)
Short description of the scoping rules?
(9 answers)
Closed 6 months ago.
If I run the following code:
x = 1
class Incr:
print(x)
x = x + 1
print(x)
print(x)
It prints:
1
2
1
Okay no problems, that's exactly what I expected. And if I do the following:
x = 1
class Incr:
global x
print(x)
x = x + 1
print(x)
print(x)
It prints:
1
2
2
Also what I expected. No problems there.
Now if I start making an increment function as follows:
x = 1
def incr():
print(x)
incr()
It prints 1 just as I expected. I assume it does this because it cannot find x in its local scope, so it searches its enclosing scope and finds x there. So far no problems.
Now if I do:
x = 1
def incr():
print(x)
x = x + 1
incr()
This gives me the following error in the traceback:
UnboundLocalError: local variable 'x' referenced before assignment.
Why does Python not just search the enclosing space for x when it cannot find a value of x to use for the assignment like my class Incr did? Note that I am not asking how to make this function work. I know the function will work if I do the following:
x = 1
def incr():
global x
print(x)
x = x + 1
print(x)
incr()
This will correctly print:
1
2
just as I expect. All I am asking is why it doesn't just pull x from the enclosing scope when the keyword global is not present just like it did for my class above. Why does the interpreter feel the need to report this as an UnboundLocalError when clearly it knows that some x exists. Since the function was able to read the value at x for printing, I know that it has x as part of its enclosing scope...so why does this not work just like the class example?
Why is using the value of x for print so different from using its value for assignment? I just don't get it.
Classes and functions are different, variables inside a class are actually assigned to the class's namespace as its attributes, while inside a function the variables are just normal variables that cannot be accessed outside of it.
The local variables inside a function are actually decided when the function gets parsed for the first time, and python will not search for them in global scope because it knows that you declared it as a local variable.
So, as soon as python sees a x = x + 1(assignment) and there's no global declared for that variable then python will not look for that variable in global or other scopes.
>>> x = 'outer'
>>> def func():
... x = 'inner' #x is a local variable now
... print x
...
>>> func()
inner
Common gotcha:
>>> x = 'outer'
>>> def func():
... print x #this won't access the global `x`
... x = 'inner' #`x` is a local variable
... print x
...
>>> func()
...
UnboundLocalError: local variable 'x' referenced before assignment
But when you use a global statement then python for look for that variable in global scope.
Read: Why am I getting an UnboundLocalError when the variable has a value?
nonlocal: For nested functions you can use the nonlocal statement in py3.x to modify a variable declared in an enclosing function.
But classes work differently, a variable x declared inside a class A actually becomes A.x:
>>> x = 'outer'
>>> class A:
... x += 'inside' #use the value of global `x` to create a new attribute `A.x`
... print x #prints `A.x`
...
outerinside
>>> print x
outer
You can also access the class attributes directly from global scope as well:
>>> A.x
'outerinside'
Using global in class:
>>> x = 'outer'
>>> class A:
... global x
... x += 'inner' #now x is not a class attribute, you just modified the global x
... print x
...
outerinner
>>> x
'outerinner'
>>> A.x
AttributeError: class A has no attribute 'x'
Function's gotcha will not raise an error in classes:
>>> x = 'outer'
>>> class A:
... print x #fetch from globals or builitns
... x = 'I am a class attribute' #declare a class attribute
... print x #print class attribute, i.e `A.x`
...
outer
I am a class attribute
>>> x
'outer'
>>> A.x
'I am a class attribute'
LEGB rule: if no global and nonlocal is used then python searches in this order.
>>> outer = 'global'
>>> def func():
enclosing = 'enclosing'
def inner():
inner = 'inner'
print inner #fetch from (L)ocal scope
print enclosing #fetch from (E)nclosing scope
print outer #fetch from (G)lobal scope
print any #fetch from (B)uilt-ins
inner()
...
>>> func()
inner
enclosing
global
<built-in function any>
From Python scopes and namespaces:
It is important to realize that scopes are determined textually: the global scope of a function defined in a module is that module’s namespace, no matter from where or by what alias the function is called. On the other hand, the actual search for names is done dynamically, at run time — however, the language definition is evolving towards static name resolution, at “compile” time, so don’t rely on dynamic name resolution! (In fact, local variables are already determined statically.)
Which means that, the scope for x = x + 1 is determined statically, before the function is called. And since this is an assignment, then 'x' becomes a local variable, and not looked up globally.
That is also the reason why from mod import * is disallowed in functions. Because the interpreter won't import modules for you in compile time to know the names you are using in the function. i.e, it must know all names referenced in the function at compile time.
It's the rule Python follows - get used to it ;-) There is a practical reason: both the compiler and human readers can determine which variables are local by looking only at the function. Which names are local has nothing to do with the context in which a function appears, and it's generally a Very Good Idea to follow rules that limit the amount of source code you have to stare at to answer a question.
About:
I assume it does this because it cannot find x in its local scope, so
it searches its enclosing scope and finds x.
Not quite: the compiler determines at compile time which names are and aren't local. There's no dynamic "hmm - is this local or global?" search going on at runtime. The precise rules are spelled out here.
As to why you don't need to declare a name global just to reference its value, I like Fredrik Lundh's old answer here. In practice, it is indeed valuable that a global statement alerts code readers to that a function may be rebinding a global name.
Because that's the way it was designed to work.
Basically, if you have an assignment anywhere in your function, then that variable becomes local to that function (unless you've used global, of course).
Because it would lead to very hard to track down bugs!
When you type that x = x + 1, you could have meant to increment an x in the enclosing scope... or you could have simply forgotten that you already used x somewhere else and been trying to declare a local variable.
I would prefer the interpreter to only allow you to change the parent namespace if you intend to- this way you can't do it by accident.
I can try and make an educated guess why it works this way.
When Python encounters a string x = x + 1 in your function, it has to decide where to look up the x.
It could say "the first occurrence of x is global, and the second one is local", but this is quite ambiguous (and therefore against Python philosophy). This could be made part of the syntax, but it potentially leads to tricky bugs. Therefore it was decided to be consistent about it, and treat all occurrences as either global or local variables.
There is an assignment, therefore if x was supposed to be global, there would be a global statement, but none is found.
Therefore, x is local, but it is not bound to anything, and yet it is used in the expression x + 1. Throw UnboundLocalError.
As an extra example to the newly created A.x created within the class. The reassigning of x to 'inner' within the class does not update the global value of x because it is now a class variable.
x = 'outer'
class A:
x = x
print(x)
x = 'inner'
print(x)
print(x)
print(A.x)

Scope of variables in python

I just come across a confusion ,let's consider the below 2 codes
first code:
def out(): #func1
print i
i=5
Output:5
Second:
def inc(): #func2
i=i+1
print i
i=5
When execiting the above programs ,func1 doesn't give any error but func2 gives an error...
error: var i referenced before assignment
I am confused that i is a local variable or global.If it is local variable then how func1 is accessing it and if it's global then why func2 is unable to access it?
That depends, when you assign a variable inside a function using the = operator, it will shadow any other outer scopes declaration of the variable and use local instead, unless it's declared global inside the function.
For example, your second try will work as you expected if you would do it like this:
def inc(): #func2
global i
i=i+1
print i
i=5
On the other side, if no assignment is performed inside a function, it will use outer scopes declaration, as you saw in your first try.
Also, if you use global i but no i is defined in outer scopes the function will raise a not defined exception as well.
You need to declare i as global in func2 to be able to modify it's value.
def func_2():
global i
i=i+1
print i
Without the global statement, you would be incrementing a variable local to func_2 without actually first giving it a value. And that's where your error comes from.
You can print the contents of global variables without declaring them global. You can also access mutable containers like lists and dictionaries without the global statment but strings, integers, floats and so forth cannot be modified without first making them global.
Examples:
>>> i = [1, 2, 3]
>>> def func_3():
... i.append(4)
... print i
>>> func_3()
[1, 2, 3, 4]
>>> i = "foo"
>>> def func_4()
... i = "bar"
... print i
>>> func_4()
"bar"
>>> print i
"foo"

How do I change nesting function's variable in the nested function

I'd like to have variable defined in the nesting function to be altered in the nested function, something like
def nesting():
count = 0
def nested():
count += 1
for i in range(10):
nested()
print count
When nesting function is called, I wish it prints 10, but it raises UnboundLocalError. The key word global may resolve this. But as the variable count is only used in the scope of nesting function, I expect not to declare it global. What is the good way to do this?
In Python 3.x, you can use the nonlocal declaration (in nested) to tell Python you mean to assign to the count variable in nesting.
In Python 2.x, you simply can't assign to count in nesting from nested. However, you can work around it by not assigning to the variable itself, but using a mutable container:
def nesting():
count = [0]
def nested():
count[0] += 1
for i in range(10):
nested()
print count[0]
Although for non-trivial cases, the usual Python approach is to wrap the data and functionality in a class, rather than using closures.
A little bit late, you can attach an attribute to "nesting" function like so:
def nesting():
def nested():
nested.count += 1
nested.count = 0
for i in range(10):
nested()
return nested
c = nesting()
print(c.count)
The most elegant approach for me: Works 100% on both python versions.
def ex8():
ex8.var = 'foo'
def inner():
ex8.var = 'bar'
print 'inside inner, ex8.var is ', ex8.var
inner()
print 'inside outer function, ex8.var is ', ex8.var
ex8()
inside inner, ex8.var is bar
inside outer function, ex8.var is bar
More: http://www.saltycrane.com/blog/2008/01/python-variable-scope-notes/

Categories

Resources