Python global and local variables - python

In Python 2.7, running the following code:
def f():
a = a + 1
f()
gives the following result:
Traceback (most recent call last):
File "test.py", line 4, in <module>
f()
File "test.py", line 2, in f
a = a + 1
UnboundLocalError: local variable 'a' referenced before assignment
But if I change the code to below:
def f():
a[0] = a[0] + 1
f()
I get the different error:
Traceback (most recent call last):
File "test.py", line 4, in <module>
f()
File "test.py", line 2, in f
a[0] = a[0] + 1
NameError: global name 'a' is not defined
Why is Python considering a is a local variable when it is an int, global when list? What's the rationale behind this?
P.S.: I was experimenting after reading this thread.

The key is found in the documentation on the assignment statement:
Assignment of an object to a single target is recursively defined as
follows.
If the target is an identifier (name) (e. g. a = a + 1):
If the name does not occur in a global statement in the current code
block: the name is bound to the object in the current local namespace.
Otherwise: the name is bound to the object in the current global
namespace.
The name is rebound if it was already bound. This may cause
the reference count for the object previously bound to the name to
reach zero, causing the object to be deallocated and its destructor
(if it has one) to be called.
...
If the target is a subscription (e. g. a[0] = a[0] + 1): The primary expression in the
reference is evaluated. It should yield either a mutable sequence
object (such as a list) or a mapping object (such as a dictionary).
Next, the subscript expression is evaluated.
In f1 Python sees that you are binding some value to a, sees that a has not been used in a global a statement in this scope and prepares a local variable. Then it attempts to evaluate the expression a + 1, looks up the variable a and finds an uninitialized local variable. This results in the UnboundLocalError.
In f2 Python sees that you are assigning some value to a subscription of the variable a. It looks up that variable in the local namespace and fails to find it. It then walks up the non-local namespaces (closures) until it reaches the global namespace. Once it fails to find a in the global namespace it throws a NameError.

Could you try to do something like this :
def f(a):
a += 1
print a
def g():
a = 3
f(a)
g()

Related

Reassigning variable in Python [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 months ago.
Improve this question
In Python, I have an expensive function a(x) which is currently evaluated multiple times within another function. At the start of that function, I want to evaluate a(x) once, and reassign the variable name a locally to that value. But I keep getting an Unbound Local Error.
As a MWE:
def a(x):
return x+1
def main(x):
a = a(x)
return a**2
main(3)
#---------------------------------------------------------------------------
#UnboundLocalError Traceback (most recent call last)
#Input In [62], in <cell line: 8>()
# 5 a = a(x)
# 6 return a**2
#----> 8 main(3)
#
#Input In [62], in main(x)
# 4 def main(x):
#----> 5 a = a(x)
# 6 return a**2
#UnboundLocalError: local variable 'a' referenced before assignment
Obviously, a workaround is to use a line like b = a(x) instead.
But why is this happening? And how can I reassign the variable name a?
The error happens because in this line:
a = a(x)
you are redefining a to be a local variable. This occurs for all uses for a within that scope, including the right hand side of the expression. a doesn't start off as a global and then become a local at some point during the function's execution at runtime; if there is an assignment to a anywhere in the function, then a is now a local everywhere in that function.
That means that when that line is actually executed, it's attempting to call the local variable a before anything has been assigned to it.
Here's a simpler demonstration of the same effect, where we aren't changing the type of a, and we aren't referencing it on the same line where we're assigning to it:
>>> a = 42
>>> def foo():
... print(a) # should be 42, right?
... if False:
... a = 42 # should be a no-op, right?
...
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in foo
UnboundLocalError: local variable 'a' referenced before assignment
In this case, the rebinding of a happens after it's used, and it happens in a block that will never even actually be executed when the function is called -- but it doesn't matter, because at the time the function is defined, the existence of that assignment makes a a local variable.
You can actually see this by inspecting foo itself, without calling it:
>>> foo.__code__.co_varnames
('a',)
Compare with an implementation that doesn't make a into a local:
>>> def foo():
... print(a)
...
>>> foo()
42
>>> foo.__code__.co_varnames
()
The error is occurring because you have already named 'a' as a function that you can't assign as a variable again instead of
a = a(x)
you can name it some other variable like 'b' like this
def main(x):
b = a(x)
return b**2
Try declaring a as global variable:
def a(x):
return x+1
def main(x):
global a # <<<<<<<< only this line need to be added.
a = a(x) # Now a is first called (right side) and then reassigned
return a**2
print(main(3))
Now it works!
Output:
16
If you check what is a now:
print(a)
Output:
4
So global a has changed. Its no more a function and it cannot be called now:
print(a(3))
Output:
TypeError: 'int' object is not callable

Unbound local variables in a class definition are looked up in the global namespace - what does it mean?

The last paragraph of https://docs.python.org/3/reference/executionmodel.html#resolution-of-names says
Class definition blocks and arguments to exec() and eval() are special in the context of name resolution. A class definition is an executable statement that may use and define names. These references follow the normal rules for name resolution with an exception that unbound local variables are looked up in the global namespace.
What does the last sentence of the quoted text mean? At first I inferred from it that the following code will print 1
a = 1
def foo():
a = 2
def bar():
class Bar:
b = a
print(Bar.b)
bar()
foo()
but I was wrong - a module consisting of the code above, when run, prints 2, i.e. the name a in the class definition, even though it's not bound in the class definition block and not bound in the local block just outside of it, isn't looked up in the global namespace, contrary to what the docs say.
I tried another code snippet described below (using a del statement, which is a construct that binds the variable in it)
a = 1
def foo():
a = 2
def bar():
class Bar:
del a
print(Bar.b)
bar()
foo()
but the del statement raises NameError: name 'a' is not defined.
So, I don't understand, what does that sentence mean?
According to the docs,
if a name is bound in a block, it is a local variable of that block, unless declared as nonlocal or global.
In your first code block, a is not bound to anything in your class Bar definition, so it is not a local variable of that block.
One way of binding a name is by using it on the left-hand side of an assignment statement. Here's an example.
a = 1
def foo():
a = 2
class Bar:
b = a
a = 3
print(Bar.b)
foo()
Result:
1
This demonstrates the principle of "unbound local variables are looked up in the global namespace" -- b = a uses the value of the global a rather than the value of the a local to foo.
In your second example, a is considered to be local to the class Bar block, because "A target occurring in a del statement is also considered bound" for the purpose of determining the scope of a name. But "unbound local variables are looked up in the global namespace" is not relevant, since del does not need to look up the value of the name in order to unbind it.
For good measure, we can experimentally confirm that a del statement signals to the interpreter that a name should be considered local.
a = 1
def foo():
a = 2
class Bar:
print(a)
del a
foo()
Result:
1
Traceback (most recent call last):
File "C:\Users\Kevin\Desktop\test.py", line 7, in <module>
foo()
File "C:\Users\Kevin\Desktop\test.py", line 4, in foo
class Bar:
File "C:\Users\Kevin\Desktop\test.py", line 6, in Bar
del a
NameError: name 'a' is not defined
Here we see that print(a) successfully looks up the value of the local variable a, and then on the next line it crashes because del can't delete an unbound local variable.
A local variable of a scope is any name defined anywhere in this scope. Notably, the variable itself is local, not the value assigned to it - a local variable can exist before or regardless of assignment.
>>> def foo(): # new function scope
... a = 3 # local variable `a`
... b: int # local variable `b`
... c = 3 # local variable `c`
... del c
... print(x)
... x = 3 # local variable `x`
... foo()
UnboundLocalError: local variable 'x' referenced before assignment
An unbound local variable is such a local variable with no value bound to it. In the above example, all of b, c and x are unbound at some point.
Neither of your examples access an unbound local variable. Both lookup the name a, but never assign to it.
In a function block, referencing unbound local variables is an error, namely UnboundLocalError. It does not matter whether that name exists in an enclosing scope as well.
>>> x = 1
>>> def foo():
... b = x # local variable is looked up locally
... x = 2 # make `x` a local variable
... foo()
UnboundLocalError: local variable 'x' referenced before assignment
In a class block, referencing unbound local variables falls back to a lookup in the global scope. This may or may not succeed.
>>> x = 1
>>> class Foo:
... b = x # local variable is looked up locally *or globally*
... x = 2 # make `x` a local variable
... print(Foo.b, Foo.x)
1 2
>>> class Foo:
... b = y # local variable is looked up locally *or globally*
... y = 2 # make `y` a local variable
... print(Foo.b, Foo.y)
NameError: name 'y' is not defined
I think I can make a supplement.
Python pre-computes which frame contains each name before executing the body of the function.
This means such phenomenon:
In [1]: a = 1
In [2]: def test():
...: print(a)
...:
In [3]: test()
1
In [4]: def test():
...: print(a)
...: a = 1
...:
In [5]: test()
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
<ipython-input-5-fbd55f77ab7c> in <module>
----> 1 test()
<ipython-input-4-a08051373573> in test()
1 def test():
----> 2 print(a)
3 a = 1
4
UnboundLocalError: local variable 'a' referenced before assignment
In [6]:
Error local variable 'a' referenced before assignment means Python pre-computes that function test frame has a local variable named a, we have to first assign one object to a and then reference it later.

python - syntax suggestion - simulation

Consider the following code simulation a non-homogenous poisson process. The focus is on the Non_hom_poisson(T,n,N) function.
def lam_t(T): #calculates lam(t) at given t
Lambda=[]
t=T
for i in range(len(T)):
Lambda.append(0.8+0.4*np.sin(0.2*np.pi*t[i]))
return Lambda
def thinning(max_min,Lam_t,lam_min,times,subset_length):
#thins max-min to lam(t)-lam_min
t=0
J=0
I=0
S=[]
path=2
t_J=np.arange(0,602,2)
while (J<300):
if (path==2):
unif=np.random.uniform(0,1)
X=(-1/float(max_min[J]))*np.log(unif)
path=3
if (path==3):
if ((t+X)>t_J[J+1]):
if ((J+2)>=300):
return S;
X=(X-t_J[J+1]+t)*max_min[J+1]/float(max_min[J+2])
t=t_J[J+1]
J+=1
path=3
else:
t+=(X)
U=np.random.uniform(0,1)
L_t=0.8+0.4*np.sin(0.2*(np.pi)*t)
top_prob=float(L_t-lam_min[J])
bottom_prob=float(max_min[J])
prob=top_prob/float(bottom_prob)
if (U<=(prob)):
I+=1
S.append(float(t))
path=2
if (t>600):
break
return S;
def mod_lam(t,lam):
interval=float(np.mod(t,10))
J=np.arange(2,12,2)
for i in range(len(J)):
if (interval<J[i]):
return float(lam[i])
return float(lam[i])
def Non_hom_poisson(T,n,N):
time=np.arange(0.1,10.1,0.1)
Lambda_t=lam_t(time)
max_lam=[max(Lambda_t[x:(x+19)]) for x in range(0,len(time),20)]
min_lam=[min(Lambda_t[x:(x+19)]) for x in range(0,len(time),20)]
max_min_lam=[(max_lam[x]-min_lam[x]) for x in range(len(max_lam))]
max_min_lam=np.tile(max_min_lam,60)
min_lam=np.tile(min_lam,60)
poisson_min=[np.random.poisson(float(min_lam[0]))]
i=0
while (poisson_min[i]<600):
y=float(mod_lam(poisson_min[i],min_lam))
x=-1/float(y)*np.log(np.random.uniform(0,1))
poisson_min.append(float(x)+float(poisson_min[i]))
i+=1
thinned=thinning(max_min_lam,Lambda_t,min_lam,time,20)
superposition=np.append(thinned,poisson_min)
return np.sort(superposition)
NH=Non_hom_poisson(600,5,1)
print(NH)
I'm getting the following error - "'int' object is not callable" - when max_lam=[max(Lambda_t[x:(x+19)]) for x in range(0,len(time),20)] is called. Any suggestion?
There are three function calls in that line: range(), max() and len(). Those are python builtins.
But neither of those identifiers are reserved words. So what's probably happened is that you have used one of those identifiers as a variable name in the global scope. Since both len() and range() are called with no error in the lam_t() function, it must be max that is an integer.
Which means that an assignment like this has been executed in the global scope.
max = 100
The code included in the question does not contain any such assignment, but the error message indicates that max points to an integer. You can reproduce the error in the python repl.
>>> max = 1
>>> max(1, 3, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
>>> del max # fix it
>>> max(1, 3, 2)
3
It's good practice to avoid using sum, len, list, max, min, int, hash, dir etc. as variable names.
It's a rich cause of confusing bugs if you are using something like a jupyter notebook where it's common practice to define lots of variables in the global namespace and then forget about it.

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).

python counter with closure

I'm trying to build a counter in python with the property of closure. The code in the following works:
def generate_counter():
CNT = [0]
def add_one():
CNT[0] = CNT[0] + 1
return CNT[0]
return add_one
However when I change the list CNT to a var, it did not work:
def generate_counter1():
x = 0
def add_one():
x = x + 1
return x
return add_one
when I print the closure property of an instance, I found the __closure__ of the second case is none:
>>> ct1 = generate_counter()
>>> ct2 = generate_counter1()
>>> print(ct1.__closure__[0])
<cell at 0xb723765c: list object at 0xb724370c>
>>> print(ct2.__closure__)
None
Just wondering why the index in outer function has to be a list?
Thanks for the answers! Found the docs which clearly explained this problem
https://docs.python.org/3/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value
Python determines the scope of a name by looking at name binding behaviour; assignment is one such behaviour (function parameters, importing, the target in for target ... or while .. as target are other examples). A name you bind to in a function is considered local. See the Naming and Binding section of the reference documentation.
So the name x in your second example is a local variable, because you assigned directly to it:
x = x + 1
In fact, because you never gave that x a local value, you'll get an exception when you try to use that function; the local name is unbound when you try to read it:
>>> generate_counter1()()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in add_one
UnboundLocalError: local variable 'x' referenced before assignment
In your first example no such binding takes place; you are instead altering the contents of CNT, what that name references is not altered.
If you are using Python 3, you can override the decision to make a name local, by using a nonlocal statement:
def generate_counter2():
x = 0
def add_one():
nonlocal x
x = x + 1
return x
return add_one
By making x non-local, Python finds it in the parent context and creates a closure for it again.
>>> def generate_counter2():
... x = 0
... def add_one():
... nonlocal x
... x = x + 1
... return x
... return add_one
...
>>> generate_counter2().__closure__
(<cell at 0x1078c62e8: int object at 0x1072c8070>,)
nonlocal is new in Python 3; in Python 2 you are limited to tricks like using a mutable list object to evade the binding rule. Another trick would be to assign the counter to an attribute of the nested function; again, this avoids binding a name in the current scope:
def generate_counter3():
def add_one():
add_one.x += 1
return add_one.x
add_one.x = 0
return add_one
It doesn't have to be a list, it just has to be an mutable object which you mutate, not reassign.
From the docs:
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.
Thus, in your second example, x is considered local (to the inner function), and, after your first assignment, you're shadowing the outer 'x'.
On the other hand, in the first example (since you don't assign a value to CNT) it operates on the CNT defined in the outer function.

Categories

Resources