The following code works as intended:
def f():
def g():
print(a)
a = 42
return g
f()()
g loads a from its closure and all is well.
Now this snippet fails horribly with UnboundLocalError.
def f():
def g():
print(a)
a = 43
a = 42
return g
f()()
Looking at the dis, the first code calls LOAD_CLOSURE and the second does not. Taking this into consideration it is obvious why the error is raised. The question however is this:
How does python know when to draw a variable from the closure or from the local scope? (Considering that print(a) precedes a = 43.)
Is this decision taken at compile time? (Well, looks like it, considering that print(a) precedes a = 43)
This post is not about the nonlocal or global keywords.
In the absence of nonlocal or global declarations, Python decides whether a variable is local at bytecode compilation time by checking the function for assignments to the variable. Since the second example assigns to a in g, a is local to g in that version.
Related
This question already has an answer here:
Where is nonlocals()?
(1 answer)
Closed 5 months ago.
I have a local variable x = "local" which unfortunately shares its name with both a global and a non-local variable. Without changing any of the names, can I access all three values? For x = "global" there is globals(), but what about the non-local variable?
Minimal example which illustrates the issue:
x = "global"
def f(x="nonlocal"):
def g():
x = "local"
print(x) # same as locals()["x"]
print(globals()["x"])
# here I want to print the non-local x
return g
f()()
I don't get your context that you have to use same name.
Anyway, you can capture outer function's locals as nonlocal variable.
x = "global"
def f(x="nonlocal"):
nonlocals = locals()
def g():
x = "local"
print(x)
print(nonlocals['x'])
print(globals()["x"])
return g
f()()
output:
local
nonlocal
global
Though you couldn't do this with the code written exactly as given, you can use inspect to get non-local variables. Note the changes to the call and return. If the caller is the outer scope instead of global scope, the previous frame will be f.
import inspect
x = "global"
def f(x="nonlocal"):
def g():
x = "local"
print(x)
print(globals()["x"])
print(inspect.currentframe().f_back.f_locals["x"])
return g()
f()
Output
local
global
nonlocal
This might not help in this specific situation, it really depends on how much control you have over the contents of f. If you don't have control over that, you can also monkey-patch f. A lot depends on context.
Edit: I didn't notice that the question specifically asked for this without using nonlocal. Leaving this here in case others find it useful.
I question the rationale behind this, but in Python 3, you can use the nonlocal keyword to access the previous scope, store that before re-declaration, then get it later.
x = "global"
def f(x="nonlocal"):
def g():
nonlocal x
y = x
x = "local"
print(x) # same as locals()["x"]
print(globals()["x"])
print(y)
return g
f()()
Output
local
global
nonlocal
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)
Closed 6 months ago.
def some_func(a):
def access_a():
print(a)
access_a()
outputs the value of a. However, if I want to change a in the nested function like this:
def some_func(a):
def change_a():
a += 1
print(a)
change_a()
it raises UnboundLocalError exception.
I know a is a nonlocal variable, but why can I access it without declaring nonlocal a?
Python scoping rules 101:
a name bound in a function body is considered local unless explicitely declared global (Python 2.x and 3.x) or nonlocal (Python 3.x only). This holds true whereever the assignment happens in the function's body. Trying to read a local variable before it's bound is of course an error.
if a name is read but not bound in a function's body, it will be looked up in enclosing scopes (outer function(s) if any then global scope). NB: functions arguments are de facto local names so they will never be looked up in enclosing scopes.
Note that a += 1 is mainly a shortcut for a = a + 1, so in your example a is local (bound in the function's body and not explicitely declared global or nonlocal), but you try to read it (the rhs of a = a+1) before it's bound.
In Python 3 you can solve this with a nonlocal statement:
>>> def outer(a):
... def change():
... nonlocal a
... a += 1
... print("before : {}".format(a))
... change()
... print ("after : {}".format(a))
...
>>> outer(42)
before : 42
after : 43
Python 2 doesn't have nonlocal so the canonical hack is to wrap the variable in a mutable container (typically a list but any mutable object would do):
>>> def outer(a):
... _a = [a]
... def change():
... _a[0] += 1
... print("before : {}".format(_a[0]))
... change()
... print ("after : {}".format(_a[0]))
...
>>> outer(42)
before : 42
after : 43
which is quite ugly to say the least.
Now while closures are quite handy, they are mostly the functional counterpart of objects : a way to share state between a set of functions while preserving encapsulation of this state, so if you find you have a need for a nonlocal variable perhaps a proper class might be a cleaner solution (though possibly not for your example that doesn't return the inner function but only uses it internally).
i have two solutions for you:
#first one:
# try with list, compound data types dict/list
def some_func(a):
def change_a():
a[0] += 1
print(a[0])
change_a()
some_func([1])
>>> 2
#second one
#reference pointer
from ctypes import *
def some_func_ctypes(a):
def change_a():
a[0] += 1
print a.contents, a[0]
change_a()
i = c_int(1)
pi = pointer(i)
some_func_ctypes(pi)
>>> c_int(2) 2
When you use the += operator, there is an assignment of a new value to a. This turns a to a local in the eyes of the interpreter.
This question already has answers here:
Modify global list inside a function
(5 answers)
Closed 6 years ago.
I was experimenting with this code:
def a():
#global p
p.append(4);
d=9
p=[2,3];
d=8
a();
print p # ----> [2, 3, 4]
print d # ----> 8
The variable d value is not changed as I didn't use the global keyword. But the list p was modified in function even though I didn't use global. Are all lists global by default in functions?
The critical difference is the assignment here. You are fine calling methods on existing global objects, but you can't assign to them without calling them global. In your code, the name d is being reassigned to reference another value. If you changed p with assignment you'd have a similar result
def a():
p = [5, 7] # new local variable, doesn't change global
p.append(9) # doesn't change global p
This makes sense if you think about what happens when python encounters the name for the first time. In the function you've provided, python will see p.append and say "hm, I don't have a local by the name p, let me look in the enclosing scope." It sees the global p and uses that.
In the example I've shown, python will say "there's no explicit global so I assume this is supposed to be a new local variable." and create one.
Names in python are just references. If python followed the behavior you are expecting you'd need a global for every function you called, let me explain:
def a():
p.append(1) # I should need 'global p' to do this
This would mean if you had
def g():
...
def f():
g() # this would also need 'global g', otherwise how does it see g?
def f2():
global g
def g(): # changes the global g function
return 0
How do global variables work in Python? I know global variables are evil, I'm just experimenting.
This does not work in python:
G = None
def foo():
if G is None:
G = 1
foo()
I get an error:
UnboundLocalError: local variable 'G' referenced before assignment
What am I doing wrong?
You need the global statement:
def foo():
global G
if G is None:
G = 1
In Python, variables that you assign to become local variables by default. You need to use global to declare them as global variables. On the other hand, variables that you refer to but do not assign to do not automatically become local variables. These variables refer to the closest variable in an enclosing scope.
Python 3.x introduces the nonlocal statement which is analogous to global, but binds the variable to its nearest enclosing scope. For example:
def foo():
x = 5
def bar():
nonlocal x
x = x * 2
bar()
return x
This function returns 10 when called.
You need to declare G as global, but as for why: whenever you refer to a variable inside a function, if you set the variable anywhere in that function, Python assumes that it's a local variable. So if a local variable by that name doesn't exist at that point in the code, you'll get the UnboundLocalError. If you actually meant to refer to a global variable, as in your question, you need the global keyword to tell Python that's what you meant.
If you don't assign to the variable anywhere in the function, but only access its value, Python will use the global variable by that name if one exists. So you could do:
G = None
def foo():
if G is None:
print G
foo()
This code prints None and does not throw the UnboundLocalError.
You still have to declare G as global, from within that function:
G = None
def foo():
global G
if G is None:
G = 1
foo()
print G
which simply outputs
1
Define G as global in the function like this:
#!/usr/bin/python
G = None;
def foo():
global G
if G is None:
G = 1;
print G;
foo();
The above python prints 1.
Using global variables like this is bad practice because: http://c2.com/cgi/wiki?GlobalVariablesAreBad
I'm wondering if it's possible for a closure in Python to manipulate variables in its namespace. You might call this side-effects because the state is being changed outside the closure itself. I'd like to do something like this
def closureMaker():
x = 0
def closure():
x+=1
print x
return closure
a = closureMaker()
a()
1
a()
2
Obviously what I hope to do is more complicated, but this example illustrates what I'm talking about.
You can't do exactly that in Python 2.x, but you can use a trick to get the same effect: use a mutable object such as a list.
def closureMaker():
x = [0]
def closure():
x[0] += 1
print x[0]
return closure
You can also make x an object with a named attribute, or a dictionary. This can be more readable than a list, especially if you have more than one such variable to modify.
In Python 3.x, you just need to add nonlocal x to your inner function. This causes assignments to x to go to the outer scope.
What limitations have closures in Python compared to language X closures?
nonlocal keyword in Python 2.x
Example:
def closureMaker():
x = 0
def closure():
nonlocal x
x += 1
print(x)
return closure