Python – Unexpected behaviour when accessing variable inside function that is defined outside function
Question:
This results in UnboundLocalError: cannot access local variable 'ans' where it is not associated with a value
.
def a():
ans = 4
def traverse():
if ans == 2:
pass
if True:
ans = 3
traverse()
print(ans)
a()
This prints out 4:
def a():
ans = 4
def traverse():
if ans == 2:
pass
traverse()
print(ans)
a()
Why is it that in the second code there is no error on the ans == 2
line but in the first case there is?
Answers:
In the first example, ans
is declared as a local variable within traverse
, but because that’s after the first time it’s accessed, you get an error:
def traverse():
if ans == 2: # no local 'ans' exists yet!
pass
if True:
ans = 3 # ans is a local var
In the second example, there is no assignment to ans
inside of traverse
, so the variable from the outer scope is used instead:
def traverse():
if ans == 2: # 'ans' refers to the outer scope
pass
What you might want to do is declare that ans
is nonlocal
, so that the assignment modifies the nonlocal var rather than creating a new var in the local scope:
def a():
ans = 4
def traverse():
nonlocal ans
if ans == 2: # accesses nonlocal ans
pass
if True:
ans = 3 # rebinds nonlocal ans
traverse()
print(ans) # prints 3
a()
This results in UnboundLocalError: cannot access local variable 'ans' where it is not associated with a value
.
def a():
ans = 4
def traverse():
if ans == 2:
pass
if True:
ans = 3
traverse()
print(ans)
a()
This prints out 4:
def a():
ans = 4
def traverse():
if ans == 2:
pass
traverse()
print(ans)
a()
Why is it that in the second code there is no error on the ans == 2
line but in the first case there is?
In the first example, ans
is declared as a local variable within traverse
, but because that’s after the first time it’s accessed, you get an error:
def traverse():
if ans == 2: # no local 'ans' exists yet!
pass
if True:
ans = 3 # ans is a local var
In the second example, there is no assignment to ans
inside of traverse
, so the variable from the outer scope is used instead:
def traverse():
if ans == 2: # 'ans' refers to the outer scope
pass
What you might want to do is declare that ans
is nonlocal
, so that the assignment modifies the nonlocal var rather than creating a new var in the local scope:
def a():
ans = 4
def traverse():
nonlocal ans
if ans == 2: # accesses nonlocal ans
pass
if True:
ans = 3 # rebinds nonlocal ans
traverse()
print(ans) # prints 3
a()