Scope question

Discussion in 'Python' started by Nitro, Aug 6, 2007.

  1. Nitro

    Nitro Guest


    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

    This gives an

    UnboundLocalError: local variable 'y' referenced before assignment

    If I change the function like this:

    y = 0
    def func():
    print y

    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?

    Nitro, Aug 6, 2007
    1. Advertisements

  2. 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 B. Roggisch, Aug 6, 2007
    1. Advertisements

  3. Nitro

    Neil Cerutti Guest

    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.

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

    But there's a trick to it. Python has the following peculiar
    ... 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

    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

    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
    Neil Cerutti, Aug 6, 2007
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.