[Python-Dev] Explicit Lexical Scoping (pre-PEP?)

Talin talin at acm.org
Tue Jul 4 09:59:50 CEST 2006


This is sort of a re-do of an earlier proposal which seems to have 
gotten lost in the shuffle of the larger debate.

I propose to create a new type of scoping rule, which I will call 
"explicit" lexical scoping, that will co-exist with the current 
"implicit" scoping rule that exists in Python today.


Definitions:

Implicit scoping is what we have now - a variable is defined within a 
scope implicitly by assignment. More specifically, when a name is 
assigned, the name is defined at the innermost function-level scope from 
where the assignment took place.

Explicit scoping is where the programmer explicitly specifies which 
scope the variable should be defined in. Unlike implicit scoping, 
assignments to a named variable do not automatically redefine that 
variable within the current scope.


Syntax:

Borrowing from Perl, the keyword 'my' is used to declare an explicitly 
scoped variable:

    def f1():
       my x = 1
       def f2():
          x = 2   # Does not create a new x

In the above example, the statement 'my x = 1' declares that the scope 
of the variable 'x' is the outer function f1. Any assignment to x will 
modify the existing x, rather than creating a new definition.

Note that the 'my' prefix can be combined with an assignment operation. 
It is anticipated that the 'my' prefix will be used quite frequently 
(and encouraged), so it makes sense to cut down on the number of 
statements by combining declaration and assignment.

Explicitly scoped variables can also be declared at the module level:

    my x = 1
    def f1():
       x = 2   # Modifies the global X

Declaring a module-level variable with an explicit scope eliminates the 
need for a 'global' statement for that variable.


Nested Scopes:

Each occurance of the keyword 'my' creates a new scope which hides any 
outer definitions of the name. So for example:

    my x = 1
    def f1():
       my x = 2  # This is a different 'x' than the global
       def f2():
          x = 3  # This is the 'x' defined within f1()


Interaction between explicit scoping and globals:

The 'global' statement, when used with explicitly scoped variables, 
means exactly the same as it does with implicitly scoped variables: It 
allows access to the outermost scope, overriding any intermediate 
definitions in surrounding scopes:

    x = 1
    def f1():
       my x = 2
       def f2():
          global x
          x = 3     # This is the module-level 'x'


Explicit scoping and code block structure:

Implicitly scoped variables are always defined at the nearest enclosing 
function scope, even if they are created within a code block.

It might be worth considering allowing explicitly scoped variables to be 
defined within other scopes. For example, we might choose to allow 
explicit scope declarations to be limited to the current suite:

    def f1():
       for x in range(0,10):
          my y = x*x          # A new definition of y for each iteration

Note that this is a speculation only, and not a core part of the 
proposal (so please don't reject the proposal on this one point.)


Formal definition:

When a value is assigned to a local variable name, the rules for 
determining which scope the variable will be defined in are as follows:

    1) Starting with the current (innermost) scope, examine all of the 
currently active scopes:
       1a) If the current scope contains a 'global' statement for the 
given name, then set the result scope to the outermost (module-level) scope.
       1b) If the current scope contains a 'my' statement for the given 
name, then set the result scope to the scope in which the 'my' statement 
occurred.
    2) Otherwise, continue until we run out of scopes. If neither a 
'global' or 'my' declaration was discovered, then use the innermost 
scope as the result scope.


How is this different from 'outer'?

The explicit scope proposal requires that the scope be specified at the 
place where the variable is *defined* as opposed to where it is *used*. 
This definition is inherited by all inner scopes.

This allows a finer degree of control, for less typing, than the 'outer' 
proposal. With explicit scoping, there is no confusion as to which scope 
is being considered; And explicit scoping allows a single declaration of 
a variable to be shared by many different inner scopes, which would 
otherwise require a separate 'outer' statement for each one.


Explicit scoping and static analysis:

It should be easier to do static analysis of code with explicit scoping, 
since you always know what scope a variable is defined in (as opposed to 
implicit scoping, where a variable may switch from global to local as a 
result of an assignment.)

Note that this implies that the creation of the scope does not occur at 
the time of the assignment, but rather at the time the function is 
entered. Thus:

    x = 1
    def f1():
       print x   # Error, unassigned value
       my x = 2

In the above example, even though the 'my' statement occurs after the 
print, the scope created by the 'my' statement is in effect for the 
entire function, although the actual *assignment* takes place after the 
print. The reason for this is that the scope creation is actually done 
by the compiler.

-- Talin


More information about the Python-Dev mailing list