[Python-Dev] Lexical scoping in Python 3k

Ka-Ping Yee python-dev at zesty.ca
Sat Jul 1 01:03:14 CEST 2006


On Fri, 30 Jun 2006, Guido van Rossum wrote:
> On 6/30/06, Ka-Ping Yee <python-dev at zesty.ca> wrote:
> > On Fri, 30 Jun 2006, Andrew Koenig wrote:
> > > I hope Py3000 has lexical scoping a la Scheme...
> >
> > Me too -- that would be really nice.
>
> That's not a very constructive proposal (especially since I don't know
> Scheme). Perhaps you could elaborate on what needs to change?

Sorry!  I should have been more clear.

Right now, Python supports the existence of nested scopes:

    a = 3
    def f():
        a = 4
        def g():
            a = 5
            print a     # prints 5
        g()
        print a         # prints 4
    f()
    print a             # prints 3

The above example shows that there are three distinct scopes, and that
each one has a distinct binding named 'a' -- assigning to one doesn't
affect the others.

    a = 3
    def f():
        b = 4
        def g():
            c = 5
            print a, b, c       # i can see all three
        g()
    f()

The above example shows that all of the scopes can be *read*.  But
in today's Python, not all of the scopes can be *written*.

    a = 3
    def f():
        b = 4
        def g():
            c = 5
            a, b, c = 0, 1, 2   # changes local c, not outer a and b
        g()
    f()

The code in g() can affect its own local, 'c', and it can affect
the global variable 'a' if it declares 'global a', but no matter
what you write in g(), it cannot assign to 'b' (or to any other
intermediate scope).

This is a strange limitation and it would be nice to remove it.
The asymmetry comes from Python having one foot in the new paradigm
of nested lexical scopes and one foot still in the older paradigm
of only two scopes, local and global.

Most other languages that support lexical scoping (including Scheme,
JavaScript, Ruby, Perl, E, Java, Smalltalk) provide a uniform way
to read and write to scopes at all levels.  This is done by letting
programmers specify the scope in which they want a variable bound
(usually with a keyword like "var" in JavaScript, "my" in Perl, or
"define" in E).

So here are some thoughts on how Python might be adjusted to support
this.  I'm not saying these would be the only ways, but at least
they're some ideas to start with.

In JavaScript, the "var" keyword is required whenever you want to
declare a local variable.  Anything without "var" is assumed to be
a global name.  The cleanest and most consistent solution that
comes to mind would be to adopt exactly this for Python.

Without "var":

    a = 3                       # global
    def f():
        b = 4                   # global
        def g():
            c = 5               # global
            a, b, c = 0, 1, 2   # changes all three globals
        g()
    f()
    print a, b, c               # prints 0, 1, 2

With "var":

    var a = 3                   # global
    def f():
        var b = 4               # local to f
        def g():
            var c = 5           # local to g
            a, b, c = 0, 1, 2   # changes outer a, outer b, and c
            print c             # prints 2
        g()
        print b                 # prints 1
    f()
    print a                     # prints 0
    print b                     # no such name
    print c                     # no such name

But that is a big change.  Perhaps it would be too unpythonic to
suddenly require declarations for all local variables.  So an
alternative would be to retain the default assumption that
undeclared variables are local.  Here's what we'd get:

Without "var":

    a = 3
    def f():
        b = 4
        def g():
            c = 5
            a, b, c = 0, 1, 2   # changes local c, not outer a and b
        g()
    f()

With "var":

    var a = 3
    def f():
        var b = 4
        def g():
            var c = 5
            a, b, c = 0, 1, 2   # changes outer a, outer b, and c
        g()
    f()

Now i think this is a little bit weird, because the statement
"var b = 4" in an outer scope changes the meaning of "b" in an
inner scope.  But it does have the virtue of retaining behaviour
compatible with today's Python, while offering a way to get proper
lexical scopes for those who want to use them.

Thoughts?  Other ideas?


-- ?!ng



More information about the Python-Dev mailing list