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/
Related
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 7 months ago.
The following code raises an UnboundLocalError:
def foo():
i = 0
def incr():
i += 1
incr()
print(i)
foo()
Is there a way to accomplish this?
Use nonlocal statement
def foo():
i = 0
def incr():
nonlocal i
i += 1
incr()
print(i)
foo()
For more information on this new statement added in python 3.x, go to https://docs.python.org/3/reference/simple_stmts.html#the-nonlocal-statement
You can use i as an argument like this:
def foo():
i = 0
def incr(i):
return i + 1
i = incr(i)
print(i)
foo()
See 9.2. Python Scopes and Namespaces:
if no global statement is in effect – assignments to names always go into the innermost scope.
Also:
The global statement can be used to indicate that particular variables live in the global scope and should be rebound there; the nonlocalstatement indicates that particular variables live in an enclosing scope and should be rebound there.
You have many solutions:
Pass i as an argument ✓ (I would go with this one)
Use nonlocal keyword
Note that in Python2.x you can access non-local variables but you can't change them.
People have answered your question but no one seems to address why exactly this is happening.
The following code raises an UnboundLocalError
So, why? Let's take a quote from the FAQ:
When you make an assignment to a variable in a scope, that variable becomes local to that scope and shadows any similarly named variable in the outer scope.
Inside your nested function you are performing an assignment with the += operator. What this means is that i += 1 will approximately perform i = i + 1 (from a binding perspective). As a result i in the expression i + 1 will be searched for in the local scope (because it is used in the assignment statement) for function incr where it will not be found, resulting in an UnboundLocalError: reference before assignment .
There's many ways you can tackle this and python 3 is more elegant in the approach you can take than python 2.
Python 3 nonlocal:
The nonlocal statements tells Python to look for a name in the enclosing scope (so in this case, in the scope of function foo()) for name references:
def foo():
i = 0
def incr():
nonlocal i
i +=1
incr()
print(i)
Function attributes Python 2.x and 3.x:
Remember that functions are first class objects, as such they can store state. Use function attributes to access and mutate function state, the good thing with this approach is it works on all pythons and doesn't require global, nonlocal statements.
def foo():
foo.i = 0
def incr():
foo.i +=1
incr()
print(foo.i)
The global Statement (Python 2.x, 3.x):
Really the ugliest of the bunch, but gets the job done:
i = 0
def foo():
def incr():
global i
i += 1
incr()
print(i)
foo()
The option of passing an argument to the function gets the same result but it doesn't relate to mutating the enclosing scope in the sense nonlocal and global are and is more like the function attributes presented. It is creating a new local variable in function inc and then re-binding the name to i with the i = incr(i) but, it does indeed get the job done.
In Python, int are immutable. So you could put your int in a mutable object.
def foo():
i = 0
obj = [i]
def incr(obj):
obj[0]+=1
incr(obj)
print(obj[0])
foo()
You can make i global and use it.
i = 0
def foo():
def incr():
global i
i += 1
incr()
print(i)
foo()
but the most preferred way is to pass i as a param to incr
def foo():
i = 0
def incr(arg):
arg += 1
return arg
i = incr(i)
print(i)
foo()
you could also use a lambda function:
def foo():
i=0
incr = lambda x: x+1
print incr(i)
foo()
I think the code is cleaner this way
Simple function attributes will not work in this case.
>>> def foo():
... foo.i = 0
... def incr():
... foo.i +=1
... incr()
... print(foo.i)
...
>>>
>>>
>>> foo()
1
>>> foo()
1
>>> foo()
1
You re assign your foo.i to 0 for every call of foo.
It better to use hassattr. But code become more complicated.
>>> def foo():
... if not hasattr(foo, 'i'):
... foo.i = 0
... def incr():
... foo.i += 1
... return foo.i
... i = incr()
... print(i)
...
>>>
>>>
>>> foo()
1
>>> foo()
2
>>> foo()
3
Also you can try this idea:
>>> def foo():
... def incr():
... incr.i += 1
... return incr.i
... incr.i = 0
... return incr
...
>>> a = foo()
>>> a()
1
>>> a()
2
>>> a()
3
>>> a()
4
May be it is more handy to wrap you incr into decorator.
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/
How do I make code like the following work? I want to reference a variable, for assignment, in the enclosing function scope.
def outer():
x = 0
def inner():
x += 1
inner()
The code as written gives an UnboundLocalError. I understand why I get this error, I just don't know how I indicate that x comes from the wrapping scope.
You can do:
def outer():
x = [0]
def inner():
x[0] += 1
inner()
You can't rebind a non-local, but you can mutate it.
You cannot do what you ask in a clean way. There is nothing analagous to the global statement that can help you. You'll want to code it like this:
def outer():
x = 0
def inner(x):
return x + 1
x = inner(x)
This has the added advantage of making it explicitly clear as to how data passes into, and out of, the function.
Perhaps you will need to replace x with an object whose state can be mutated.
I am reading this article about decorator.
At Step 8 , there is a function defined as:
def outer():
x = 1
def inner():
print x # 1
return inner
and if we run it by:
>>> foo = outer()
>>> foo.func_closure # doctest: +ELLIPSIS
it doesn't print x. According to the explanation :
Everything works according to Python’s scoping rules - x is a local
variable in our function outer. When inner prints x at point #1 Python
looks for a local variable to inner and not finding it looks in the
enclosing scope which is the function outer, finding it there.
But what about things from the point of view of variable lifetime? Our
variable x is local to the function outer which means it only exists
while the function outer is running. We aren’t able to call inner till
after the return of outer so according to our model of how Python
works, x shouldn’t exist anymore by the time we call inner and perhaps
a runtime error of some kind should occur.
However, I don't really understand what the second paragraph means.
I understand inner() does get the value of x but why it doesn't print x out?
thanks
UPDATE:
Thanks all for the answers. Now I understand the reason.
the "return inner" is just a pointer to inner() but it doesn't get executed, that is why inner() doesn't print x as it is not called at all
I understand inner() does get the value of x but why it doesn't print
x out?
It doesn't print out anything because you've not called the inner function yet.
>>> def outer():
x = 1
def inner():
print x # 1
return inner
...
>>> func = outer()
>>> func
<function inner at 0xb61e280c>
>>> func()
1
This is called a closure, i.e even though the outer function is not in stack(finished executing) anymore but still the inner function that was returned from it remembers it's state.(i.e value of x)
>>> def outer():
x = 1
y = 2
def inner():
z=3
print x
return inner
...
>>> func = outer()
>>> func.func_code.co_freevars #returns the variables that were used in closure
('x',)
From the source code on how python decides it's a closure or not:
459 if len(code.co_freevars) == 0:
460 closure = NULL
461 else:
462 len(closure) == len(code.co_freevars)
In py3.x you can also modify the value of x using nonlocal statement inside inner function.
>>> def outer():
x = 1
def inner():
nonlocal x
x += 1
print (x)
return inner
...
>>> func = outer()
>>> func()
2
>>> func()
3
>>> func()
4
You are not calling inner. You have called outer, which returns inner, but without calling it. If you want to call inner, do foo() (since you assinged the result of outer() to the name foo).
The paragraph you quoted is sort of tangential to this issue. You say you already understand why inner gets the value of x, which is what that paragraph is explaining. Basically, if a local variable is used in a nested function, and that nested function is returned, the value of the variable is stored along with the returned function, even if the scope where that variable was defined in no longer active. Normally x would be gone after outer finished, because x is just local to outer. But outer returns inner, which still needs access to x. So x gets wrapped up into what's called a closure, so it can still be accessed by inner later on.
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)