Nested scopes, and augmented assignment

Piet van Oostrum piet at cs.uu.nl
Wed Jul 5 11:32:40 EDT 2006


>>>>> Antoon Pardon <apardon at forel.vub.ac.be> (AP) wrote:

>AP> On 2006-07-05, Bruno Desthuilliers <onurb at xiludom.gro> wrote:
>>> Antoon Pardon wrote:
>>> (snip)
>>>> Well no matter what explanation you give to it, and I understand how it
>>>> works,
>>> 
>>> I'm not sure of this.

>AP> Should I care about that?

Yes, because as long as you don't understand it, you are in for unpleasant
surprises. 

>>> It's not about "finding a name/identifier", it's about the difference
>>> between (re)binding a name and mutating an object.

>AP> The two don't contradict each other. Python has chosen that it won't
>AP> rebind variables that are out of the local scope. So if the lefthand
>AP> side of an assignment is a simple name it will only search in the
>AP> local scope for that name. But if the lefthand side is more complicated
>AP> if will also search the outerscopes for the name.

No. It will always use the same search order. But a variable that is bound
inside the function (with an asignment) and is not declared global, is in
the local namespace. A variable that is not assigned to inside the function
is not in the local namespace. An assignment to A[i] is not rebinding A, so
it doesn't count to make the variable A local.

>AP> Python could have chosen an approach with a "nested" keyword, to allow
>AP> rebinding a name in an intermediate scope. It is not that big a deal
>AP> that it hasn't, but I keep finding the result strange and somewhat
>AP> counterintuitive.

Maybe it would have been nice if variables could have been declared as
nested, but I think it shows that nested variables have to be used with
care, similar to globals. Especially not allowing rebinding in intermediate
scopes is a sound principle (`Nested variables considered harmful').
If you need to modify the objects which are bound to names in intermediate
scopes, use methods and give these objects as parameters.

>AP> Let me explain why:

>AP> Suppose someone is rather new of python and writes the following
>AP> code, manipulating vectors:

>AP>   A = 10 * [0]
>AP>   def f(B):
>AP>     ...
>AP>     for i in xrange(10):
>AP>       A[i] += B[i]
>AP>     ...

>AP> Then he hears about the vector and matrix modules that are around.
>AP> So he rewrites his code naively as follows:

>AP>   A = NullVector(10):
>AP>   def f(B):
>AP>     ...
>AP>     A += B
>AP>     ...

>AP> And it won't work. IMO the principle of least surprise here would
>AP> be that it should work.

Well, A = f(B) introduces a local variable A. Therefore also  A = A+B.
And therefore also A += B.

The only way to have no surprises at all would be if local variables would
have to be declared explicitly, like 'local A'. But that would break
existing code. Or maybe the composite assignment operators should have been
exempted. Too late now!

You have the same problem with:

    A = [10]
    def inner():
        A.append(2)

works but 

    A = [10]
    def inner():
        A += [2]

doesn't. The nasty thing in this example is that although A += [2] looks
like a rebinding syntactically, semantically there is no rebinding done.

I think the cleaner solution is (use parameters and don't rebind):
    def inner(A):
        A.append(2)

-- 
Piet van Oostrum <piet at cs.uu.nl>
URL: http://www.cs.uu.nl/~piet [PGP 8DAE142BE17999C4]
Private email: piet at vanoostrum.org



More information about the Python-list mailing list