Scope in 2.2.1

Alex Martelli aleax at aleax.it
Sat May 11 06:36:57 EDT 2002


On Saturday 11 May 2002 11:38 am, David LeBlanc wrote:
> Alex;
>
> Thanks for your comments.
>
> I am confused by the last part though: you say that "bar2 += 1" is a
> reference before a rebinding or alteration of an already bound variable.
> Well, bar2 _is_ already bound by the logic you used in the discussion of
> bar in the print statement!

It's not yet bound in fee's scope : since it's subject to a binding operation
in fee, it's a local variable of fee.  Name 'bar2' IS bound in other scopes,
but that's irrelevant, since a local variable hides all other homonyms.

> What would happen if, instead of "print bar", it said "bar += 1" or "print
> bar++"? As python works now, it would seem that "bar += 1 is an error, but
> i'm not sure about bar++, since it's a post-fetch increment. It seems

This would give a SyntaxError before any scoping issues get raised:-).

> inconsistant to me that a reference (without modification) finds an outer
> binding in a print statement, but an implicit (attempt to) reference in a
> plus-equals is an error.

The "reference (without modification)" would find nothing at all if there
WERE binding operations on that name anywhere else in the block.

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.

> It only makes sense to me to not use foo.bar2 within fee() if there's a

foo.bar2 is not "the local variable bar2 of function foo" -- it's an attribute
named bar2 of object foo.  Local variables are not attributes of any such
object.  If several calls to a function are simultaneously active, each
has its own set of locals, quite separate from each other -- but if the
function object has an attribute bar2, that's quite independent from any
calls to the function, whether there be active one, none, or several.

> local bar2 or a "global bar2" (which would then have to refer to a global
> bar2, not foo.bar2 no?). As you say, "+=" is a reference and rebinding (if
> that's the appropriate terminology). Since the reference comes before the
> rebinding, the nearest pre-existing binding of bar2 ought to be used. I

Nope.  When 'x' is a local variable of the block, ALL uses of 'x' in the
block are uses of that local variable.

> think this is a bug! I think it at least violates the principle of least
> surprise :-)

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).


> 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.

> 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.


Alex





More information about the Python-list mailing list