confusing UnboundLocalError behaive

N

neoedmund

see the 3 small piece of code, i cannot understand why it result as
this.

1.
def test():
abc="111"
def m1():
print(abc)
m1()
test()

Output: 111

2.
def test():
abc="111"
def m1():
print(abc)
abc+="222"
m1()
test()

Output:
print(abc)
UnboundLocalError: local variable 'abc' referenced before assignment

3.
def test2():
abc=[111]
def m1():
print(abc)
abc.append(222)
m1()
print(abc)
test2()

Output:
[111]
[111,222]

it seems "you cannot change the outter scope values but can use it
readonly."
 
C

Chris Rebert

see the 3 small piece of code, i cannot understand why it result as
this.

1.
def test():
abc="111"
def m1():
print(abc)
m1()
test()

Output: 111

2.
def test():
abc="111"
def m1():

You need a 'nonlocal' declaration here (requires Python 3.0 I think).
See PEP 3104 for more info -- http://www.python.org/dev/peps/pep-3104/
print(abc)
abc+="222"
m1()
test()

Output:
print(abc)
UnboundLocalError: local variable 'abc' referenced before assignment

3.
def test2():
abc=[111]
def m1():
print(abc)
abc.append(222)
m1()
print(abc)
test2()

Output:
[111]
[111,222]

it seems "you cannot change the outter scope values but can use it
readonly."

Yeah, that's basically how nested scopes (sans 'nonlocal') work in
Python, since assignment typically constitutes an implicit scope
declaration.

Cheers,
Chris
 
G

Gabriel Genellina

it seems "you cannot change the outter scope values but can use it
readonly."

Exactly.
Python doesn't have variable "declarations" - so the compiler uses this
rule: "if the variable is assigned to, anywhere in the function body, it's
local". This is done by static analysis when the code is compiled.
2.
def test():
abc="111"
def m1():
print(abc)
abc+="222"
m1()
test()

Output:
print(abc)
UnboundLocalError: local variable 'abc' referenced before assignment

abc is assigned to, so it is local (and different from the abc in its
enclosing scope). You can't print abc until it is assigned "something".
3.
def test2():
abc=[111]
def m1():
print(abc)
abc.append(222)
m1()
print(abc)

No assignment to abc, so it's not local; print(abc) starts looking for it
in all the enclosing scopes (up to the module global scope, and last, the
builtin module).
 
S

Steven D'Aprano

see the 3 small piece of code, i cannot understand why it result as
this.

1.
def test():
abc="111"
def m1():
print(abc)
m1()
test()

Output: 111

abc is local to test(). print(abc) looks for a local abc, can't find one,
and so searches the higher scope, and finds it there.


2.
def test():
abc="111"
def m1():
print(abc)
abc+="222"
m1()
test()

Output:
print(abc)
UnboundLocalError: local variable 'abc' referenced before assignment


Because you make an assignment to abc inside the m1() function, but
didn't declare it as global, Python assumes that it must be a local
variable. So when you try to print it, it doesn't have a value yet.

Solution: don't do that, or use the statement nonlocal (like global,
except I think it is only introduced in Python 3.0).

3.
def test2():
abc=[111]
def m1():
print(abc)
abc.append(222)
m1()
print(abc)
test2()

Output:
[111]
[111,222]

it seems "you cannot change the outter scope values but can use it
readonly."

But you're not using it read-only, because the append worked.

What you can't do is assign to the name.


Have a look at the disassembled code:
.... print x
.... y = x+1
.... 2 0 LOAD_GLOBAL 0 (x)
3 PRINT_ITEM
4 PRINT_NEWLINE

3 5 LOAD_GLOBAL 0 (x)
8 LOAD_CONST 1 (1)
11 BINARY_ADD
12 STORE_FAST 0 (y)
15 LOAD_CONST 0 (None)
18 RETURN_VALUE.... print x
.... x = x+1
.... 2 0 LOAD_FAST 0 (x)
3 PRINT_ITEM
4 PRINT_NEWLINE

3 5 LOAD_FAST 0 (x)
8 LOAD_CONST 1 (1)
11 BINARY_ADD
12 STORE_FAST 0 (x)
15 LOAD_CONST 0 (None)
18 RETURN_VALUE
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,813
Latest member
lawrwtwinkle111

Latest Threads

Top