
On Thu, 22 Feb 2001, Guido van Rossum wrote:
Note that this is moot now -- see my previous post about how we've decided to resolve this using a magical import to enable nested scopes (in 2.1).
Yes, yes. It seems like a good answer for now -- indeed, some sort of mechanism for selecting compilation options has been requested before. But we still need to eventually have a coherent answer. The chart in my other message doesn't look coherent to me -- it would take too long to explain all of the cases to someone. I deserve a smack on the head for my confusion at seeing 'x' printed out -- that happens to be the value of the NameError in 1.5.2. Here is an updated chart (updated test script is attached): 1.5.2 2.1a2 suggested toplevel with print x from foo import * 3 1 3 1 3 1 exec('x = 1') 3 1 3 1 3 1 x = 1 3 1 3 1 3 1 with g() from foo import * 3 1 3 1 3 1 exec('x = 1') 3 1 3 1 3 1 x = 1 3 1 3 1 3 1 x = 3 outside f() with print x from foo import * 3 1 3 1 3 1 exec('x = 1') 3 1 3 1 3 1 x = 1 NameError UnboundLocal 3 1 with g() from foo import * 3 3 SyntaxError 3 1 exec('x = 1') 3 3 SyntaxError 3 1 x = 1 NameError UnboundLocal 3 1 x = 3 inside f() with print x from foo import * 3 1 3 1 3 1 exec('x = 1') 3 1 3 1 3 1 x = 1 3 1 3 1 3 1 with g() from foo import * NameError SyntaxError 3 1 exec('x = 1') NameError SyntaxError 3 1 x = 1 NameError 3 1 3 1 You can see that the situation in 1.5.2 is pretty messy -- and it's precisely the inconsistent cases that have historically caused confusion. 2.1a2 is better but it still has exceptional cases -- just the cases people seem to be complaining about now.
There is something missing from my understanding here:
- The model is, each environment has a pointer to the enclosing environment, right?
Actually, no.
I'm talking about the model, not the implementation. I'm advocating that we think *first* about what the programmer (the Python user) has to worry about. I think that's a Pythonic perspective, isn't it? Or are you really saying that this isn't even the model that the user should be thinking about?
- Whenever you can't find what you're looking for, you go up to the next level and keep looking, right?
That depends. Our model is inspired by the semantics of locals in Python 2.0 and before, and this all happens at compile time.
Well, can we nail down what you mean by "depends"? What reasoning process should the Python programmer go through to predict the behaviour of a given program?
In particular:
x = 1 def f(): print x x = 2
raises an UnboundLocalError error at the point of the print
I've been getting the impression that people consider this a language wart (or at least a little unfortunate, as it tends to confuse people). It's a frequently asked question, and when i've had to explain it to people they usually grumble. As others have pointed out, it can be pretty surprising when the assignment happens much later in the body. I think if you asked most people what this would do, they would expect 1. Why? Because they think about programming in terms of some simple invariants, e.g.: - Editing part of a block doesn't affect the behaviour of the block up to the point where you made the change. - When you move some code into a function and then call the function, that code still works the same. This kind of backwards-action-at-a-distance breaks the first invariant. Lexical scoping is good largely because it helps preserve the second invariant (the function carries the context of where it was defined). And so on.
No need to go to the source -- this is all clearly explained in the PEP (http://python.sourceforge.net/peps/pep-0227.html).
It seems not to be that simple, because i was unable to predict what situations would be problematic without understanding how the optimizations are implemented. * * *
5. The current scope is determined by the nearest enclosing 'def'.
For most purposes, 'class' also creates a scope.
Sorry, i should have written: 5. The parent scope is determined by the nearest enclosing 'def'. * * *
Given this model, all of the scoping questions that have been raised have completely clear answers:
Example I [...] Example II You didn't try this, did you? [...] Example III Wrong again. [...] Example IV I didn't try this one, but I'm sure that it prints 3 in 2.1a1 and raises the same SyntaxError as above with the current CVS version.
I know that. I introduced these examples with "given this model..." to indicate that i'm describing what the "completely clear answers" are. The chart above tries to summarize all of the current behaviour.
But the ability of the compiler to make this optimization should only affect performance, not affect the Python language model.
Too late. The semantics have been bent since 1.0 or before.
I think it's better to try to bend them as little as possible -- and if it's possible to unbend them to make the language easier to understand, all the better. Since we're changing the behaviour now, this is a good opportunity to make sure the model is simple.
The model described above [...] exactly reflects the environment-diagram model of scoping as taught to most Scheme students and i would argue that it is the easiest to explain.
I don't know Scheme, but isn't it supposed to be a compiled language?
That's not the point. There is a scoping model that is straightforward and easy to understand, and regardless of whether the implementation is interpreted or compiled, you can easily predict what a given piece of code is going to do.
I'm not sure how you can say that Scheme sidesteps the issue when you just quote an example where Scheme implementations differ?
That's what i'm saying. The standard sidesteps (i.e. doesn't specify how to handle) the issue, so the implementations differ. I don't think we have the option of avoiding the issue; we should have a clear position on it. (And that position should be as simple to explain as we can make it.)
I see that Tim posted another rebuttal, explaining better than I do here *why* Ping's "simple" model is not good for Python, so I'll stop now.
Let's get a complete specification of the model then. And can i ask you to clarify your position: did you put quotation marks around "simpler" because you disagree that the model i suggest is simpler and easier to understand; or did you agree that it was simpler but felt it was worth compromising that simplicity for other benefits? And if the latter, are the other benefits purely about enabling optimizations in the implementation, or other things as well? Thanks, -- ?!ng