129 lines
4.8 KiB
Plaintext
129 lines
4.8 KiB
Plaintext
|
PEP: 227
|
|||
|
Title: Statically Nested Scopes
|
|||
|
Version: $Revision$
|
|||
|
Author: jeremy@digicool.com (Jeremy Hylton)
|
|||
|
Status: Draft
|
|||
|
Type: Standards Track
|
|||
|
Python-Version: 2.1
|
|||
|
Created: 01-Nov-2000
|
|||
|
Post-History:
|
|||
|
|
|||
|
Abstract
|
|||
|
|
|||
|
This PEP proposes the addition of statically nested scoping
|
|||
|
(lexical scoping) for Python 2.1. The current language definition
|
|||
|
defines exactly three namespaces that are used to resolve names --
|
|||
|
the local, global, and built-in namespaces. The addition of
|
|||
|
nested scopes would allow resolution of unbound local names in
|
|||
|
enclosing functions' namespaces.
|
|||
|
|
|||
|
One consequence of this change that will be most visible to Python
|
|||
|
programs is that lambda statements could reference variables in
|
|||
|
the namespaces where the lambda is defined. Currently, a lambda
|
|||
|
statement uses default arguments to explicitly creating bindings
|
|||
|
in the lambda's namespace.
|
|||
|
|
|||
|
|
|||
|
Notes
|
|||
|
|
|||
|
This section describes several issues that will be fleshed out and
|
|||
|
addressed in the final draft of the PEP. Until that draft is
|
|||
|
ready, please direct comments to the author.
|
|||
|
|
|||
|
This change has been proposed many times in the past. It has
|
|||
|
always been stymied by the possibility of creating cycles that
|
|||
|
could not be collected by Python's reference counting garbage
|
|||
|
collector. The additional of the cycle collector in Python 2.0
|
|||
|
eliminates this concern.
|
|||
|
|
|||
|
Guido once explained that his original reservation about nested
|
|||
|
scopes was a reaction to their overuse in Pascal. In large Pascal
|
|||
|
programs he was familiar with, block structure was overused as an
|
|||
|
organizing principle for the program, leading to hard-to-read
|
|||
|
code.
|
|||
|
|
|||
|
Greg Ewing developed a proposal "Python Nested Lexical Scoping
|
|||
|
Enhancement" in Aug. 1999[1]
|
|||
|
|
|||
|
Michael Hudson's bytecodehacks projects[2] provides facilities to
|
|||
|
support nested scopes using the closure module.
|
|||
|
|
|||
|
Examples:
|
|||
|
|
|||
|
def make_adder(n):
|
|||
|
def adder(x):
|
|||
|
return x + n
|
|||
|
return adder
|
|||
|
add2 = make_adder(2)
|
|||
|
add2(5) == 7
|
|||
|
|
|||
|
|
|||
|
from Tkinter import *
|
|||
|
root = Tk()
|
|||
|
Button(root, text="Click here",
|
|||
|
command = lambda : root.test.configure(text="..."))
|
|||
|
|
|||
|
|
|||
|
One controversial issue is whether it should be possible to modify
|
|||
|
the value of variables defined in an enclosing scope.
|
|||
|
|
|||
|
One part of the issue is how to specify that an assignment in the
|
|||
|
local scope should reference to the definition of the variable in
|
|||
|
an enclosing scope. Assignment to a variable in the current scope
|
|||
|
creates a local variable in the scope. If the assignment is
|
|||
|
supposed to refer to a global variable, the global statement must
|
|||
|
be used to prevent a local name from being created. Presumably,
|
|||
|
another keyword would be required to specify "nearest enclosing
|
|||
|
scope."
|
|||
|
|
|||
|
Guido is opposed to allowing modifications (need to clarify
|
|||
|
exactly why). If you are modifying variables bound in enclosing
|
|||
|
scopes, you should be using a class, he says.
|
|||
|
|
|||
|
The problem occurs only when a program attempts to rebind the name
|
|||
|
in the enclosing scope. A mutable object, e.g. a list or
|
|||
|
dictionary, can be modified by a reference in a nested scope; this
|
|||
|
is an obvious consequence of Python's reference semantics. The
|
|||
|
ability to change mutable objects leads to an inelegant
|
|||
|
workaround: If a program needs to rebind an immutable object,
|
|||
|
e.g. a number or tuple, store the object in a list and have all
|
|||
|
references to the object use this list:
|
|||
|
|
|||
|
def bank_account(initial_balance):
|
|||
|
balance = [initial_balance]
|
|||
|
def deposit(amount):
|
|||
|
balance[0] = balance[0] + amount
|
|||
|
def withdraw(amount):
|
|||
|
balance[0] = balance[0] - amount
|
|||
|
return deposit, withdraw
|
|||
|
|
|||
|
I would prefer for the language to support this style of
|
|||
|
programming directly rather than encouraging programs to use this
|
|||
|
somewhat obfuscated style. Of course, an instance would probably
|
|||
|
be clearer in this case.
|
|||
|
|
|||
|
One implementation issue is how to represent the environment that
|
|||
|
stores variables that are referenced by nested scopes. One
|
|||
|
possibility is to add a pointer to each frame's statically
|
|||
|
enclosing frame and walk the chain of links each time a non-local
|
|||
|
variable is accessed. This implementation has some problems,
|
|||
|
because access to nonlocal variables is slow and causes garbage to
|
|||
|
accumulate unnecessarily. Another possibility is to construct an
|
|||
|
environment for each function that provides access to only the
|
|||
|
non-local variables. This environment would be explicitly passed
|
|||
|
to nested functions.
|
|||
|
|
|||
|
|
|||
|
References
|
|||
|
|
|||
|
[1] http://www.cosc.canterbury.ac.nz/~greg/python/lexscope.html
|
|||
|
|
|||
|
[2] http://sourceforge.net/projects/bytecodehacks/
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Local Variables:
|
|||
|
mode: indented-text
|
|||
|
indent-tabs-mode: nil
|
|||
|
End:
|