queue = ['one']
def f():
print queue
queue = ['one', 'two']
print queue
f()
Select the text below for a correct answer:
Traceback (most recent call last):
File "quiz.py", line 8, in
f()
File "quiz.py", line 4, in f
print queue
UnboundLocalError: local variable 'queue' referenced before assignment
Surprised? I certainly was.
Well, what about this one:
queue = ['one']
def f():
print queue
queue.append('two')
print queue
f()
This time, the script behaves as expected:
['one']
['one', 'two']
To achieve the same result in the first example, a
global statement must be added at the beginning of f():queue = ['one']
def f():
global queue
print queue
queue = ['one', 'two']
print queue
f()
The most interesting thing is what happens now:
queue = ['one']
def f():
print queue
f()
We get:
['one']without the
global statement!For the explanation of this strange behaviour, let's get back to the first example:
queue = ['one']It seems that
def f():
print queue
queue = ['one', 'two']
print queue
f()
f() treats the queue variable in the first print queue statement already as a local variable, although assignment to it is done no earlier than in the second line. This becomes reasonable if we realise that a Python method "knows" a list of its local variables before execution. This list is prepared when parsing the function definition and before the function is called. If you want to test it, you can write:print f.func_code.co_varnamesbefore calling
f() in the preceding examples. You'll notice that the output is () if we use the global statement or don't assign to queue inside the function, and (queue,) otherwise. An assignment to a variable which is not an argument of the function is treated as initialisation of a local variable -- hence the UnboundLocalError.I encountered this error today while searching for a cause of a very strange bug. It would have been very hard to find if I hadn't run my favourite static code-checker tool: PyLint. I encourage everyone to use it, and it integrates well with PyDev, too. PyLint, when encounters the buggy code from the first example, throws a very nice warning:
W0621: 5:f: Redefining name 'queue' from outer scope (line 1)...and everything becomes obvious.
3 comments:
Technically it is not an error. It may be strange, but it is documented behavior.
I blogged about it myself here
paddy3118: I didn't say it was an error -- it's just something you wouldn't guess when looking at it for the first time.
Oh I agree. I wrote my piece to help me remember it, I guess like you have :-)
Post a Comment