Scope in 2.2.1

David LeBlanc whisper at oz.net
Sat May 11 07:12:21 EDT 2002


<snip>
> You quoted the rule, and it's very simple: are there binding operations
> on that name in this block?  If so, it's a local variable of the
> block, and
> no attempts in this block to reference that local variable will
> ever 'find'
> anything but that local variable.
>
<snip>
> Having the same name refer to completely different scopes in such
> uses as:
>
> def outer():
>   x = 23
>   def inner():
>     for i in range(2):
>         print x
>         x = 45
>
> would do more than surprise me -- it would utterly ASTONISH me.
> The 'x' in "print x" needs to be compiled into one kind of reference --
> what kind will it be?  Python chooses "the local variable of inner",
> since it sees that inner has a local variable of that name.  Ignoring
> the local variable altogether would be astounding, as would be
> using the non-local on one leg of the loop and the local on the next.
>
> When nested lexical scopes were about to be introduced, I suggested
> we adopt Java's rule: it's illegal to "shadow" a variable of an
> outer block
> by a homonym in an inner block.  I thought and still think that this is a
> reasonable rule -- unfortunately almost nobody agreed, and so we have
> these situations in which there's just no way out from astonishing SOME
> people, it seems.  With the shadowing-forbidden rule, the compiler could
> give clear error messages about violations (at least for inner vs outer,
> unfortunately not for globals, for backwards compatibility at least, but
> also because the compiler can have no idea of what global names will
> be bound at runtime, while locals _are_ under the compiler's control).

I have grown accustomed by long use to the way that C, C++ and Pascal (and
asm for that matter!) become aware of declarations. To use an analogy,
there's a line that moves down the page and things below the line are
unknown to the compiler since they're not seen yet unless they're forward
declared. Code is parsed based on what's known above the line. Python seems
to have an implicit per block pre-pass that gets all the bindings before
statements are parsed. Is this the correct idea? I can see from the below
discussion that it might not be that simple, but what IS the correct
conceptual model here?

> > Maybe the appendix would make more sense as:
> >
> > "If a name binding operation occurs anywhere within a code
> block, all uses
> > of that name within the block are treated as references to that binding.
>
> They're not necessarily references to THAT binding -- not in the sense in
> which Python uses 'binding'.
>
> def f(x):
>     if x==23: x=45
>     print x
>
> the x in the print may reference either the same binding as given by f's
> caller, or the binding to 45.  It IS certainly a reference to the *local
> variable* named x, but what binding of that local variable applies, it's
> anybody's guess.  "scoping" might be a useful neologism here.

gaaaaa! this is mad! How can you write a sane program in this sort of
environment?!?

> > This will lead to errors when a name is used within a block before it is
> > bound."
>
> It sure will, and even that's not saying enough:
>
> def f():
>   x = 45
>   print x
>   del x
>   print x
>
> the second print WILL say something about "before being bound", but
> we can see it's a lie -- x WAS bound earlier, and will NOT be bound
> later, so "before" is clearly inapplicable here.
>
> Pretty hard to document this or find the perfect error message, though.

Well, this makes a kind of sense: the del returns x to it's before bound
state: unbound.

> Alex

I can hardly wait for someone to write up a _good_ comprehensive discussion
on scopes, nesting and bindings thereof, and how to live among them. What
you've described here seems, absent the correct conceptual framework (if
there even is one), to make it virtually impossible to avoid some very
subtle and nasty bugs.

Dave






More information about the Python-list mailing list