scoping weirdness

Eyal Lotem eyal at hyperroll.com
Sat Aug 25 15:08:41 EDT 2001


Paul Rubin wrote:

>     from __future__ import nested_scopes   # python 2.1.1
> 
>     def foo (n):
>         a = lambda: 'you said (%d)' % n
>         n += 3
>         return a
> 
>     x = foo(1)
>     print x()
> 
> This prints 4, rather than 1 as I would have expected.
> I think I understand what's going on, but is it really what was intended?
> Is there a preferred way of writing this kind of function?
> 
> Thanks.

There are two issues:

A) What does the nested scope 'n' refer to: the object that 'n' evaluates 
at the external scope at the time of the lambda definition, or is it the 
same 'n' reference as in the external scope.
B) Is the function's scope 'n' reference the same reference as the given 
parameter.

I think the answer to question B is obvious since far before nested scopes 
were introduced:

def f(n):
  n = n + 1
a = 1
f(a)

was never expected to increase a, thus: the function's scope 'n' reference 
is NOT the same reference as the given parameter.

The answer to A is that the internal-scope reference 'n' IS the same 
reference as the external scope 'n', and reassigning the 'n' reference in 
the external scope means that the internal-scope 'n' reference is changed.
Another possible behaviour would be to 'emulate' argument-type behaviour in 
nested scopes, so that when defining the lambda, the named references seem 
to be given by argument, and assigned to refer to the same objects as the 
external scope.  This is utterly bogus, and would require all the symbol 
names in the nested scope to exist in the external scope at the time of 
definition of the lambda, which stands in contrast to the dynamic approach 
of delaying the binding to the last possible time.

These two answers explain the result of the code you have.
The reference to the '1' object given to the function 'foo' is distinct 
from the reference 'n'.  The reference 'n' is set to refer to the object 
'4' in function 'foo'. The internal-function reference 'n' IS the 
internal-lambda reference 'n', which means that when reassigning 'n' to 4 
the internal-lambda reference is also refering to 4, which explains the 
resulted behaviour.

I think this behaviour is the only way to go, otherwise we're contradicting 
A or B which would contrast strong Python ideas.

Note that this is all based on my understanding of nested scopes and their 
implementation, which is limited to a very small experience with them.  
However, I do feel I have an understanding of the issue.




More information about the Python-list mailing list