[Python-Dev] Is outlawing-nested-import-* only an implementation issue?

Ka-Ping Yee ping@lfw.org
Fri, 23 Feb 2001 04:32:59 -0800 (PST)


  This message is in MIME format.  The first part should be readable text,
  while the remaining parts are likely unreadable without MIME-aware tools.
  Send mail to mime@docserver.cac.washington.edu for more info.

--8323328-269764679-982931579=:13155
Content-Type: TEXT/PLAIN; charset=US-ASCII

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

--8323328-269764679-982931579=:13155
Content-Type: TEXT/PLAIN; charset=US-ASCII; name="scopetest.py"
Content-Transfer-Encoding: BASE64
Content-ID: <Pine.LNX.4.10.10102230432590.13155@localhost>
Content-Description: 
Content-Disposition: attachment; filename="scopetest.py"

aW1wb3J0IHN5cw0KDQpmaWxlID0gb3BlbignZm9vLnB5JywgJ3cnKQ0KZmls
ZS53cml0ZSgneCA9IDEnKQ0KZmlsZS5jbG9zZSgpDQoNCnRvcGxldmVsID0g
IiIiDQp4ID0gMw0KcHJpbnQgeCwNCiVzDQolcw0KJXMNCiIiIg0KDQpvdXRz
aWRlID0gIiIiDQp4ID0gMw0KZGVmIGYoKToNCiAgICBwcmludCB4LA0KICAg
ICVzDQogICAgJXMNCiAgICAlcw0KZigpDQoiIiINCg0KaW5zaWRlID0gIiIi
DQpkZWYgZigpOg0KICAgIHggPSAzDQogICAgcHJpbnQgeCwNCiAgICAlcw0K
ICAgICVzDQogICAgJXMNCmYoKQ0KIiIiDQoNCmZvciB0ZW1wbGF0ZSBpbiBb
dG9wbGV2ZWwsIG91dHNpZGUsIGluc2lkZV06DQogICAgZm9yIHByaW50MSwg
cHJpbnQyIGluIFsoJ3ByaW50IHgnLCAnJyksICgnZGVmIGcoKTogcHJpbnQg
eCcsICdnKCknKV06DQogICAgICAgIGZvciBzdGF0ZW1lbnQgaW4gWydmcm9t
IGZvbyBpbXBvcnQgKicsICdleGVjKCJ4ID0gMSIpJywgJ3ggPSAxJ106DQog
ICAgICAgICAgICBjb2RlID0gdGVtcGxhdGUgJSAoc3RhdGVtZW50LCBwcmlu
dDEsIHByaW50MikNCiAgICAgICAgICAgICMgcHJpbnQgY29kZQ0KICAgICAg
ICAgICAgdHJ5Og0KICAgICAgICAgICAgICAgIGV4ZWMgY29kZSBpbiB7fQ0K
ICAgICAgICAgICAgZXhjZXB0Og0KICAgICAgICAgICAgICAgIHByaW50IHN5
cy5leGNfdHlwZSwgc3lzLmV4Y192YWx1ZQ0KICAgICAgICAgICAgcHJpbnQN
Cg==
--8323328-269764679-982931579=:13155--