the demise of 'from foo import * and its implications?

Tim Peters tim.one at home.com
Sat Mar 3 14:02:24 EST 2001


[Robin Becker]
> my preference would be that statements legal in one context should be
> legal in another where they make sense. I cannot see how using import *
> or eval etc in nested scopes is somehow obviously bad or different from
> the same usage in non-nested scopes. The fact that the prohibition comes
> from making things easier for the compiler adds to my confusion.

The prohibition (against import* except at module scope) is there for "make
sense" reasons.  Here's a simple function, where nested scopes don't even
come into play:

def f():
    from mystery import *
    g()

def g():
    print "hmm"

f()

What does that do?  Forget the compiler!  What makes *sense*?

There simply isn't an answer that isn't surprising (although there *could*
have been!).  The Ref Man has always said:

    Whether a name is local or global in a code block is determined
    by static inspection of the source text for the code block: in
    the absence of global statements, a name that is bound anywhere
    in the code block is local in the entire code block; all other
    names are considered global.

By that lovely rule, "g" is a global name within "f" (because static
inspection shows that g is not bound within the body of f).  However, because
of the way Python was first implemented, it just so happens that *if* the
mystery module contains its own global g, *that's* what "g" inside "f" refers
to instead.  The import* magically creates a local "g" inside "f", shadowing
the global g anyone reading the quoted part of the Ref Man expects (and
assuming they ignored the part of the Ref Man saying import* has undefined
behavior here).  And that's very surprising if you think-- as Guido intended,
and as the Ref Man says --that Python is statically scoped.

OTOH, since the first implementation did just happen to have this other
behavior, Guido clung to it over the years for backward compatibility,
despite that it's documented as being undefined, and despite that the
compiler internals have gotten considerably warped *trying* to preserve this
accidental behavior.  So taking it away now is surprising if you think-- as
some protest ever so loudly --that accidents should be preserved in loving
detail forever more.

It's not "a feature" of Python that it exhibits a bizarre form of dynamic
scoping in some cases.  It wasn't intended.  Whenever that pops up, it's a
flaw in the language design, or (in the case of import* at other than module
scope) a (IMO, with hindsight) misguided attempt to preserve accidents of the
initial implementation.

> I have no particular objection to being able to access variables in
> intermediate scopes.
>
> If we really want to improve visibility we could try to make
>
> >>> def bingo(a):
> ...     if a<=1: return 0
> ...     return bingo(a-1)*a
> ...
>
> work naively, but I'm fairly sure this will be rejected as unpythonic or
> somesuch.

Sorry, I can't make out what you're trying to say there.  It's a vanilla
recursive function, and it works exactly the way I expect it to work (which
hasn't changed since the first release, and almost certainly never will).
What about it doesn't work the way *you* expect?

Note that if there's a "from mystery import *" at *module* scope, and mystery
happens to define a global of its own named "bingo", then that latter is the
bingo called inside the body.  That's an example of "flaw in the language
design" I mentionoed above, and I don't give a rat's ass about preserving
*that* accident of the implementation either.

language-behaviors-are-clarified-by-relating-them-to-animal-body-
    parts-ly y'rs  - tim





More information about the Python-list mailing list