Nested scopes: why is it weird?

Alex Martelli aleax at aleax.it
Fri Sep 7 12:57:39 EDT 2001


"Scott Long" <scott at swiftview.com> wrote in message
news:3B98F3B0.53AC2524 at swiftview.com...
> Nested scopes seem like a nice addition (of course!) but there are some
> weird things about it. Take a look:

Not really, just a typical misunderstanding (which also existed
wrt global variables before nested scopes).


> Right, this is what you would expect. But how about this?
>
> >>> from __future__ import nested_scopes
> >>> def a():
> ...   def b():
> ...     k = x
> ...     x = 0

The x name in procedure b is LOCAL -- the compiler knows it,
because x is re-bound in b's scope.  (Incidentally, you can
never rebind a name from a lexically-nested local scope
in your scope; you CAN rebind a global name, if you use the
global statement at the start of a function).

> My first beef here is, why is the exception called UnboundLocalError?

Because x is a local name, and it is not yet bound when it is
first used in b's first instruction.

> The variable is certainly bound (into the local scope), it simply has

Nope.  x is a local name, but not bound to any value, as yet.
Only the assignment statement (if it was executed) would bind
name x (to the value 0).

> not been assigned. Why not call it UninitializedLocalError? I know it
> isn't going to change, I'm just asking.
>
> But my main concern is that this way of selecting a binding seems very
> unintuitive. Shouldn't the first statement referencing x be the
> statement that specifies the binding of x? In this case, shouldn't "k =

*In general*, how could the compiler tell what the first statement
IS, that _references_ name x within a given scope?  Assuming you
mean, "first to execute".  The compiler _might_ assign some
arbitrary ordering, of course.  But having:
    if a>b:
        y=x
    else:
        x=y
have *TOTALLY* different semantics from:
    if b>=a:
        x=y
    else:
        y=x
when it SEEMS they're totally equivalent, would be truly weird, so
I don't think you mean "first in some arbitrary order (such as,
longest-first, or lower-line-number-first, or whatever)".

> x" cause x to get bound as a free variable, not a local to b()? I'm not
> claiming that this is a bug. I am curious as to why it was decided that
> things are going to work this way. It makes assignment to a variable in
> an enclosing scope impossible!

Yes, the latter is exactly Guido's decision: he has always maintained
that the right (aka obvious) way for something to keep and update
state is to define a class, rather than stretch closures to let code
in them re-bind things "partways up" -- or so I hear, and I sure
see his point.  But it's not any kind of necessary consequence of
Python's long-standing rules that all variables re-bound in a scope
are local to that scope unless explicitly mentioned in a global
statement -- it would have been trivial to say, for example, that
"global x in a" was THE way for b to inform the compiler that the
x name b uses and re-binds is the one in a, not one local to b itself
(somebody wanting to make closures really powerful/handy might have
stretched that to "global x in a as z":-).

Java just doesn't let a local variable 'shadow' one with the
same name from a lexically-outer scope, and that might have
been a nice choice too -- just forbid b to have a local variable
named x if a lexically-outer scope of b had a similarly named
local variable.  Unfortunately, this restriction, which I have
always liked, and proposed when nested scope were PEP'd, was
not accepted (though it did get a mention in the PEP), so we'll
live forevermore with misunderstandings on this point.


Alex








More information about the Python-list mailing list