I was playing around with closures in Python and I do not understand why the following does not work and how to make it work:
>>> def make_counter():
... i = 0
... def inner():
... i += 1
... return i
... return inner
...
>>> c = make_counter()
>>> c()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in inner
UnboundLocalError: local variable 'i' referenced before assignment
Could anybody shed some light on it?
Thanks.
In the inner function, the statement
i += 1
can be understood like this
i = i + 1
since you are assigning a value to i in this function, Python thinks that you are creating a new variable scoped to this current function. And then when it executes the right hand side expression, i + 1, it finds that i has not been assigned any value before using it. That is why it is throwing
UnboundLocalError: local variable 'i' referenced before assignment
To fix this, you need to explicitly tell Python that you are not creating a new variable, instead, you are accessing a variable from the outer scope, with nonlocal (Python 3.x) like this
>>> def make_counter():
... i = 0
... def inner():
... nonlocal i
... i += 1
... return i
... return inner
...
>>>
>>> make_counter()()
1
Note: If you are using Python 2.x, follow any of the methods mentioned in this question
Related
In python, I know that functions run in the context that they are called from, not the context that they are defined in. However, in the code below, where a function is returned from another function, it still has access to the variables (specifically the list 'cache') of the function it was defined in, even though that function is now out of scope (I think), and so I would have though it's variables would have been deleted. But it can also access global variables from the current context?
def wrapper(func):
cache = []
def func_new(n):
cache.append(func(n))
print(cache)
return func_new
def add1(n):
return(n+1)
x = wrapper(add1)
x(5)
x(2)
print(cache)
It doesn't?
>>> x = wrapper(add1)
>>> x(5)
[6]
>>> x(2)
[6, 3]
>>> print(cache)
Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
print(cache)
NameError: name 'cache' is not defined
As for the cache variable being defined within the wrapper, that matches behavior elsewhere; the function has access to all variables where it is defined. The opposite is not true; variables defined in the function are not accessible in the context scope unless explicitly a global.
Does this example help?
>>> x = 4
>>> def test():
print(x)
>>> test()
4
>>> x = 1
>>> def f():
... print x
...
>>> f()
1
>>> x = 1
>>> def f():
... x = 3
... print x
...
>>> f()
3
>>> x
1
>>> x = 1
>>> def f():
... print x
... x = 5
...
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in f
UnboundLocalError: local variable 'x' referenced before assignment
>>> x = 1
>>> def f():
... global x
... print x
... x = 5
... print x
...
>>> f()
1
5
>>> x
5
How to treat the variable "x" inside the function as local without altering the global one when I have print statement above the variable assignment?
I expect the result of "x" to be 5 inside the function and the global x should be unaltered and remains the same in value (i.e) 1
I guess, there is no keyword called local in python contrary to global
>>> x = 1
>>> def f():
... print x
... global x
... x = 5
...
<stdin>:3: SyntaxWarning: name 'x' is used prior to global declaration
In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a value anywhere within the function’s body, it’s assumed to be a local unless explicitly declared as global.
Source.
It's true there's no local keyword in Python; instead, Python has this rule to decide which variables are local.
Any variable in your function is either local or global. It can't be local in one part of the function and global in another. If you have a local variable x, then the function can't access the global x. If you want a local variable while accessing the global x, you can call the local variable some other name.
The behaviour is already what you want. The presence of x = inside the function body makes x a local variable which entirely shadows the outer variable. You're merely trying to print it before you assign any value to it, which is causing an error. This would cause an error under any other circumstance too; you can't print what you didn't assign.
This question already has answers here:
Python nested functions variable scoping [duplicate]
(10 answers)
Closed 5 years ago.
I have a two nested python functions, which look like this:
def outer():
t = 0
def inner():
t += 1
print(t)
inner()
Trying to call outer results in the following error:
>>> outer()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "sally.py", line 6, in outer
inner()
File "sally.py", line 4, in inner
t += 1
UnboundLocalError: local variable 't' referenced before assignment
I thought adding the line global t before t += 1 would help, except it didn't.
Why is this happening? How do I get around this problem, other than passing t to inner everytime I call it?
If using python 3, then using the nonlocal keyword would let the interpreter know to use the outer() function's scope for t:
def outer():
t = 0
def inner():
nonlocal t
t += 1
print(t)
inner()
If using python 2, then you can't directly assign to the variable, or it will make the interpreter create a new variable t which will hide the outer variable. You could pass in a mutable collection and update the first item:
def outer():
t = [0]
def inner():
t[0] += 1
print(t[0])
inner()
Need help with understanding the following sentence from PEP 227 and the Python Language Reference
If a variable is referenced in an enclosed scope, it is an error to
delete the name. The compiler will raise a SyntaxError for 'del
name'.
Lack of examples caused I couldn't reproduce an error at compile time, so an explanation with examples is highly desirable.
The following raises the execption:
def foo():
spam = 'eggs'
def bar():
print spam
del spam
because the spam variable is being used in the enclosed scope of bar:
>>> def foo():
... spam = 'eggs'
... def bar():
... print spam
... del spam
...
SyntaxError: can not delete variable 'spam' referenced in nested scope
Python detects that spam is being referenced in bar but does not assign anything to that variable, so it looks it up in the surrounding scope of foo. It is assigned there, making the del spam statement a syntax error.
This limitation was removed in Python 3.2; you are now responsible for not deleting nested variables yourself; you'll get a runtime error (NameError) instead:
>>> def foo():
... spam = 'eggs'
... def bar():
... print(spam)
... del spam
... bar()
...
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in foo
File "<stdin>", line 4, in bar
NameError: free variable 'spam' referenced before assignment in enclosing scope
An example can be this one:
>>> def outer():
... x = 0
... y = (x for i in range(10))
... del x
...
SyntaxError: can not delete variable 'x' referenced in nested scope
Basically it means you can't delete variables that are used in inner blocks(in that case the genexp).
Note that this apply for python <= 2.7.x and python < 3.2.
In python3.2 it's it does not raise syntax error:
>>> def outer():
... x = 0
... y = (x for i in range(10))
... del x
...
>>>
See this link for the whole story of the change.
I think the python3.2 semanthics is more correct because if you write the same code outside a function it works:
#python2.7
>>> x = 0
>>> y = (x for i in range(10))
>>> del x
>>> y.next() #this is what I'd expect: NameError at Runtime
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <genexpr>
NameError: global name 'x' is not defined
While putting the same code into a function, not only changes exception but the error is at compile time.
>>> import sys
>>> print(sys.version)
2.4.4
>>> b = 11
>>> def foo2():
... a = b
... print a, b
...
>>> foo2()
11 11
>>> def foo3():
... a = b
... b = 12
... print a, b
...
>>> foo3()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in foo3
UnboundLocalError: local variable 'b' referenced before assignment
>>> def foo4():
... global b
... a = b
... b = 12
... print a, b
...
>>> foo4()
11 12
Question> In foo3, why you can access global variable without declaring it but you still cannot modify it.
Without a global declaration, the Python compiler scans the whole code of each function to see which variables are assigned to within the function code. In foo3(), you assign to both a and b so therefore they are both treated as local variables within the function.
When the method code executes, at the point where you do a = b, b does not have a value yet (because you have not assigned anything to it). Therefore, you get an UnboundLocalError.
This is done so that the use of a variable within a function always refers to the same location, even if nothing has been assigned to it yet.
Accidentally stomping on a global variable is a frequent source of error. So it makes sense to declare a variable global before you modify it.
Having to always declare a non-local variable global is a pain. So you're allowed to just use a global variable if all you do is read it. But if you're going to modify x anywhere in your function, you must declare it global before you do anything with it.