Scope question

N

Nitro

Hello,

today I wrote this piece of code and I am wondering why it does not work
the way I expect it to work. Here's the code:

y = 0
def func():
y += 3
func()

This gives an

UnboundLocalError: local variable 'y' referenced before assignment

If I change the function like this:

y = 0
def func():
print y
func()

then no error is thrown and python apparently knows about 'y'.

I don't understand why the error is thrown in the first place. Can
somebody explain the rule which is causing the error to me?

-Matthias
 
D

Diez B. Roggisch

Nitro said:
Hello,

today I wrote this piece of code and I am wondering why it does not work
the way I expect it to work. Here's the code:

y = 0
def func():
y += 3
func()

This gives an

UnboundLocalError: local variable 'y' referenced before assignment

If I change the function like this:

y = 0
def func():
print y
func()

then no error is thrown and python apparently knows about 'y'.

I don't understand why the error is thrown in the first place. Can
somebody explain the rule which is causing the error to me?

The reason is that python's scoping rules have to somehow determine if a
variable is local to a function or part of the surrounding context.

Because python lacks variable declarations, what is does to infer the scope
of a variable is to check if it is part of the left-side of an assignment.

So in the first case, y is considered a func-local variable - which of
course you didn't initialize.

This behavior has been subject of quite a few critical discussions - yet you
have to live with it.



diez
 
N

Neil Cerutti

Hello,

today I wrote this piece of code and I am wondering why it does
not work the way I expect it to work. Here's the code:

y = 0
def func():
y += 3
func()

This gives an

UnboundLocalError: local variable 'y' referenced before
assignment

If I change the function like this:

y = 0
def func():
print y
func()

then no error is thrown and python apparently knows about 'y'.

I don't understand why the error is thrown in the first place.
Can somebody explain the rule which is causing the error to
me?

What you're seeing is a reasonable compromise in design.

Assignments in Python, conceptually, create a new binding.

The name x is created, and bound to an object that represents
five. Any old bindings for x are discarded.

x = 12
x = 5

After the second assignment, x is bound to 5.

The mapping from names to values, in a computer language, is
usually called an "environment". Imagine the environment as a
Python dictionary. The environment starts out containing Python's
builtin names and their values, but for the sake of clearer
discussion we'll pretend it starts out empty.

The += operator looks like it mutates an object, but (for most
object types) it does not. 'x += 5' is equivalent to 'x = x + 5'
In other words, the value of x is looked up in the current
environment, five is added to it, and then the name x is rebound to
that new object. I'll be rewriting += as a "read+rebinding" in
the rest of this discussion.

Python has lexical scope. That means that the extent of a name is
limited to the lexical "space" in which it is defined.

After the definition of foo is complete, there's no binding for z
in the global environment. That's because z is defined only
inside function foo.

So you see that functions have their own environment. In the
above example, the environement of foo is {z: <int 12>}. A
statement or expression inside a function foo will first look
names in it's own environment.

If the name is not found in the functions environment, is is then
looked up in the enclosing environement.
5

An assignment or binding construct in a function changes the
*function's* environment.
12

But there's a trick to it. Python has the following peculiar
behavior:
... print x # foo's env is {}
Traceback (most recent call last):
...
UnboundLocalError: local variable 'x' referenced before assignment

The simplified rules for name lookup I gave earlier say that when
executing 'print x' Python ought to look up 'x' in foo's
environment, fail to find it, and then look up 'x' in the
enclosing environment instead.

But Python doesn't do that. If there's an assignment of a name
anywhere in the function, Python will refuse to look up that name
in the enclosing environment, insisting that it must be "local"
to the function.
this. ;)

In the above example, Python "looks ahead" and sees the
assignment to 'x' in foo, and reasons that x must be a local
variable. To rewrite foo's environment, we have to imagine that
there's a special value that x is bound to meaning "undefined
local variable" when foo is first created. Attempting to read the
value from the environment raises an exception.
Traceback (most recent call last):
...
UnboundLocalError: local variable 'x' referenced before assignment

When 'print x' is encountered, Python looks up 'x' in foo's
environment, finding <variable undefined>, and so raises an
exception.

Going back to your examples, and adding the model environments:
Traceback (most recent call last):
...
UnboundLocalError: local variable 'y' referenced before assignment

I rewrite 'y += 3' as 'y = y + 3' to make it clearer that Python
must look up y in func's env before the assignment. The
assignment never takes place because looking up y in func's
0

In the above case, Python attempts to look up y in func's
environment, fails to find it, and so looks it up in the outer
environment, where y is bound to <int 0>.

The reason Python does this peculiar thing is that functions
don't really have their own fully-fledged environements, the way
that modules and classes do. They use an--I presume--simpler,
leaner, more efficient construct. One requirement of this simpler
construct seems to be that a name must be either defined or
undefined inside a function. It can't be defined at one time, and
undefined at another time, as can happen in more full-featured
environments.
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top