[Python-Dev] Lexical scoping in Python 3k

Talin talin at acm.org
Mon Jul 3 09:27:04 CEST 2006


Josiah Carlson wrote:
> I had hoped that there would be a response to my second (and I believe
> more applicable statement); "if the feature is really only useful for
> generally trivial cases *without* the feature, then making them even
> more trivial, I think, is a bit of over optimization."

It really depends on how common the trivial case is. In other words, 
multiply the savings for each occurance times the number of occurances. 
(Unfortunately, I don't know what units to measure said savings in - is 
there a unit of 'mental disconnect' or unintuitiveness? :)

In an idealy world, the language would allow everything to be said in 
the most comprehensible way possible. Longer and more verbose ways of 
stating something are at an inherent disadvantage in this, simply 
because of the time it takes to scan and absorb the information by the 
human brain. However, losing excess syntax has to be done in a way that 
doesn't also lose information. Highly compressed representations of a 
concept may require such a level of abstraction that it is as much work 
to puzzle out their meaning as it would be to read the longer version 
and more.

To put it another way - I am an advocate of applying Claude Shannon's 
theory of information to language design. The highest level of 
compression should be used for expressions that occur the most frequently.

> As for a solution, I find the "global means 'not local'" proposition is
> the least undesireable of the possibilities.  It suffers from a change
> in semantics and potential name masking issues, but I don't believe
> these are any more serious than normal global masking for the former,
> and the latter is solvable with a __future__ (at least for 2.6). I'm a
> solid -0 on this particular proposition, which is far better than the -1
> I am on all of the other recent lexical scoping propositions.

I'd say that the more common case is where you want global to really 
mean global - that is, you want to be able to write to some module-level 
variable, regardless of how deeply nested your function scope is. While 
being able to access the 'next outer scope' is occasionally useful, it's 
not all that common. So changing the behavior of 'global' in this case 
would be both confusing (since it no longer means 'global'), and less 
useful (because it doesn't match the most common case.)

(This assumes that I haven't completely understood the meaning of the 
phrase 'not local' - I assumed that it means 'not defined in this scope')

Of course, the reason why it's not all that common may be because of the 
fact that it's not as easy to do, and so people tend to (consciously or 
otherwise) avoid that pattern in their designs. That being said, I don't 
think that's necessarily such a bad thing. Python isn't Scheme, and the 
scoping rules of Python are IMHO more oriented towards practicality and 
common sense than theoretical purity.

This is why I'm not bothered by the fact that Python doesn't create a 
new scope for loop statements and such. Most of the time, this is what 
you want. It does mean that you need to name all of your variables 
uniquely, but that's good programming style in any case. The same is 
true for local variables not needing to be specially declared as 'my' or 
'var' - most of the time, a local variable is what you want.

On the other hand, the thing about theoretical purity is that it can be 
so mouth-wateringly powerful at times. For example, a language that 
supports closures is, IMHO, at least twice as powerful as a language 
that doesn't -- because you can use them in so many different and 
interesting ways.

OK, so about the lexical scoping issue - let me brainstorm a moment:

One idea would be to introduce the keyword 'local' which would have the 
effect of capturing any 'global' statements in any enclosing scope. So 
for example:

    f = 1
    def a():
       local f
       f = 2
       def b():
          global f
          f = 3

So in this case, the 'global' statement, which would normally associate 
'f' with the outermost (module-level) scope, would instead associate 'f' 
with the innermost 'local' declaration of that variable. So in the above 
example, assigning 3 to f assigns it to the middle scope, but does not 
affect the module-level definition.

Admittedly, that's a bit confusing and also verbose, considering that 
you are not only adding an extra keyword, but also using two statements 
to specify the home of one variable.

Another alternative would be a way to declare an explicitly scoped 
variable. Lets use the keyword 'my' to indicate this:

    f = 1
    def a():
       my f = 2
       def b():
          f = 3

In this case, what the 'my' statement is doing is indicating that this 
scope 'owns' the definition of 'f' -- in other words, the definition is 
hoisted out of any enclosed scopes. So again, in the above example, the 
innermost assignment will be to the definition of 'f' in the middle scope.

What's interesting about this is that you can use the same method with 
globals:

    my f = 1
    def a():
       f = 2

    a()
    print f   # prints '2'

So again, you are indicating that the global scope 'owns' the definition 
of 'f', and any enclosed scopes should use that definition, and not 
create their own.

Of course, if you really *do* need to have your own version, you can 
always override the 'my' statement with another 'my' statement:

    my f = 1
    def a():
       my f = 2

    a()
    print f   # prints '1'

The 'my' statement essentially changes the scoping rules for all 
variables of that name, within the defining scope and all enclosed scopes.

Of course, you can also override this behavior using the 'global' 
statement, which does exactly what it does now - makes the reference 
global (i.e. module-level):

    my f = 1
    def a():
       global f
       f = 2

    a()
    print f   # prints '2'

All right, I'm pretty happy with that. Brainstorming done. :)

-- Talin


More information about the Python-Dev mailing list