Add PEP 3104 (Access to Names in Outer Scopes).
This commit is contained in:
parent
2ad9f52224
commit
84fbeafdc9
|
@ -0,0 +1,537 @@
|
|||
PEP: 3104
|
||||
Title: Access to Names in Outer Scopes
|
||||
Version: $Revision$
|
||||
Last-Modified: $Date: 2006-11-09 11:52:55 -0800 (Thu, 9 Nov 2006) $
|
||||
Author: Ka-Ping Yee <ping@zesty.ca>
|
||||
Status: Draft
|
||||
Type: Standards Track
|
||||
Python-Version: 3.0
|
||||
Content-Type: text/x-rst
|
||||
Created: 12-Oct-2006
|
||||
|
||||
|
||||
Abstract
|
||||
========
|
||||
|
||||
In most languages that support nested scopes, code can refer to or
|
||||
rebind (assign to) any name in the nearest enclosing scope.
|
||||
Currently, Python code can refer to a name in any enclosing scope,
|
||||
but it can only rebind names in two scopes: the local scope (by
|
||||
simple assignment) or the module-global scope (using a ``global``
|
||||
declaration).
|
||||
|
||||
This limitation has been raised many times on the Python-Dev mailing
|
||||
list and elsewhere, and has led to extended discussion and many
|
||||
proposals for ways to remove this limitation. This PEP summarizes
|
||||
the various alternatives that have been suggested, together with
|
||||
advantages and disadvantages that have been mentioned for each.
|
||||
|
||||
|
||||
Rationale
|
||||
=========
|
||||
|
||||
Before version 2.1, Python's treatment of scopes resembled that of
|
||||
standard C: within a file there were only two levels of scope, global
|
||||
and local. In C, this is a natural consequence of the fact that
|
||||
function definitions cannot be nested. But in Python, though
|
||||
functions are usually defined at the top level, a function definition
|
||||
can be executed anywhere. This gave Python the syntactic appearance
|
||||
of nested scoping without the semantics, and yielded inconsistencies
|
||||
that were surprising to some programmers -- for example, a recursive
|
||||
function that worked at the top level would cease to work when moved
|
||||
inside another function, because the recursive function's own name
|
||||
would no longer be visible in its body's scope. This violates the
|
||||
intuition that a function should behave consistently when placed in
|
||||
different contexts. Here's an example::
|
||||
|
||||
def enclosing_function():
|
||||
def factorial(n):
|
||||
if n < 2:
|
||||
return 1
|
||||
return n * factorial(n - 1) # fails with NameError
|
||||
print factorial(5)
|
||||
|
||||
Python 2.1 moved closer to static nested scoping by making visible
|
||||
the names bound in all enclosing scopes (see PEP 227). This change
|
||||
makes the above code example work as expected. However, because any
|
||||
assignment to a name implicitly declares that name to be local, it is
|
||||
impossible to rebind a name in an outer scope (except when a
|
||||
``global`` declaration forces the name to be global). Thus, the
|
||||
following code, intended to display a number that can be incremented
|
||||
and decremented by clicking buttons, doesn't work as someone familiar
|
||||
with lexical scoping might expect::
|
||||
|
||||
def make_scoreboard(frame, score=0):
|
||||
label = Label(frame)
|
||||
label.pack()
|
||||
for i in [-10, -1, 1, 10]:
|
||||
def increment(step=i):
|
||||
score = score + step # fails with UnboundLocalError
|
||||
label['text'] = score
|
||||
button = Button(frame, text='%+d' % i, command=increment)
|
||||
button.pack()
|
||||
return label
|
||||
|
||||
Python syntax doesn't provide a way to indicate that the name
|
||||
``score`` mentioned in ``increment`` refers to the variable ``score``
|
||||
bound in ``make_scoreboard``, not a local variable in ``increment``.
|
||||
Users and developers of Python have expressed an interest in removing
|
||||
this limitation so that Python can have the full flexibility of the
|
||||
Algol-style scoping model that is now standard in many programming
|
||||
languages, including JavaScript, Perl, Ruby, Scheme, Smalltalk,
|
||||
C with GNU extensions, and C# 2.0.
|
||||
|
||||
It has been argued that that such a feature isn't necessary, because
|
||||
a rebindable outer variable can be simulated by wrapping it in a
|
||||
mutable object::
|
||||
|
||||
class Namespace:
|
||||
pass
|
||||
|
||||
def make_scoreboard(frame, score=0):
|
||||
ns = Namespace()
|
||||
ns.score = 0
|
||||
label = Label(frame)
|
||||
label.pack()
|
||||
for i in [-10, -1, 1, 10]:
|
||||
def increment(step=i):
|
||||
ns.score = ns.score + step
|
||||
label['text'] = ns.score
|
||||
button = Button(frame, text='%+d' % i, command=increment)
|
||||
button.pack()
|
||||
return label
|
||||
|
||||
However, this workaround only highlights the shortcomings of existing
|
||||
scopes: the purpose of a function is to encapsulate code in its own
|
||||
namespace, so it seems unfortunate that the programmer should have to
|
||||
create additional namespaces to make up for missing functionality in
|
||||
the existing local scopes, and then have to decide whether each name
|
||||
should reside in the real scope or the simulated scope.
|
||||
|
||||
Another common objection is that the desired functionality can be
|
||||
written as a class instead, albeit somewhat more verbosely. One
|
||||
rebuttal to this objection is that the existence of a different
|
||||
implementation style is not a reason to leave a supported programming
|
||||
construct (nested scopes) functionally incomplete. Python is
|
||||
sometimes called a "multi-paradigm language" because it derives so
|
||||
much strength, practical flexibility, and pedagogical power from its
|
||||
support and graceful integration of multiple programming paradigms.
|
||||
|
||||
A proposal for scoping syntax appeared on Python-Dev as far back as
|
||||
1994 [1]_, long before PEP 227's support for nested scopes was
|
||||
adopted. At the time, Guido's response was:
|
||||
|
||||
This is dangerously close to introducing CSNS [classic static
|
||||
nested scopes]. *If* you were to do so, your proposed semantics
|
||||
of scoped seem allright. I still think there is not enough need
|
||||
for CSNS to warrant this kind of construct ...
|
||||
|
||||
After PEP 227, the "outer name rebinding discussion" has reappeared
|
||||
on Python-Dev enough times that it has become a familiar event,
|
||||
having recurred in its present form since at least 2003 [2]_.
|
||||
Although none of the language changes proposed in these discussions
|
||||
have yet been adopted, Guido has acknowledged that a language change
|
||||
is worth considering [12]_.
|
||||
|
||||
|
||||
Other Languages
|
||||
===============
|
||||
|
||||
To provide some background, this section describes how some other
|
||||
languages handle nested scopes and rebinding.
|
||||
|
||||
JavaScript, Perl, Scheme, Smalltalk, GNU C, C# 2.0
|
||||
--------------------------------------------------
|
||||
|
||||
These languages use variable declarations to indicate scope. In
|
||||
JavaScript, a lexically scoped variable is declared with the ``var``
|
||||
keyword; undeclared variable names are assumed to be global. In
|
||||
Perl, a lexically scoped variable is declared with the ``my``
|
||||
keyword; undeclared variable names are assumed to be global. In
|
||||
Scheme, all variables must be declared (with ``define`` or ``let``,
|
||||
or as formal parameters). In Smalltalk, any block can begin by
|
||||
declaring a list of local variable names between vertical bars.
|
||||
C and C# require type declarations for all variables. For all these
|
||||
cases, the variable belongs to the scope containing the declaration.
|
||||
|
||||
Ruby (as of 1.8)
|
||||
----------------
|
||||
|
||||
Ruby is an instructive example because it appears to be the only
|
||||
other currently popular language that, like Python, tries to support
|
||||
statically nested scopes without requiring variable declarations, and
|
||||
thus has to come up with an unusual solution. Functions in Ruby can
|
||||
contain other function definitions, and they can also contain code
|
||||
blocks enclosed in curly braces. Blocks have access to outer
|
||||
variables, but nested functions do not. Within a block, an
|
||||
assignment to a name implies a declaration of a local variable only
|
||||
if it would not shadow a name already bound in an outer scope;
|
||||
otherwise assignment is interpreted as rebinding of the outer name.
|
||||
Ruby's scoping syntax and rules have also been debated at great
|
||||
length, and changes seem likely in Ruby 2.0 [25]_.
|
||||
|
||||
|
||||
Overview of Proposals
|
||||
=====================
|
||||
|
||||
There have been many different proposals on Python-Dev for ways to
|
||||
rebind names in outer scopes. They all fall into two categories:
|
||||
new syntax in the scope where the name is bound, or new syntax in
|
||||
the scope where the name is used.
|
||||
|
||||
New Syntax in the Binding (Outer) Scope
|
||||
---------------------------------------
|
||||
|
||||
Scope Override Declaration
|
||||
''''''''''''''''''''''''''
|
||||
|
||||
The proposals in this category all suggest a new kind of declaration
|
||||
statement similar to JavaScript's ``var``. A few possible keywords
|
||||
have been proposed for this purpose:
|
||||
|
||||
- ``scope x`` [4]_
|
||||
- ``var x`` [4]_ [9]_
|
||||
- ``my x`` [13]_
|
||||
|
||||
In all these proposals, a declaration such as ``var x`` in a
|
||||
particular scope S would cause all references to ``x`` in scopes
|
||||
nested within S to refer to the ``x`` bound in S.
|
||||
|
||||
The primary objection to this category of proposals is that the
|
||||
meaning of a function definition would become context-sensitive.
|
||||
Moving a function definition inside some other block could cause any
|
||||
of the local name references in the function to become nonlocal, due
|
||||
to declarations in the enclosing block. For blocks in Ruby 1.8,
|
||||
this is actually the case; in the following example, the two setters
|
||||
have different effects even though they look identical::
|
||||
|
||||
setter1 = proc { | x | y = x } # y is local here
|
||||
y = 13
|
||||
setter2 = proc { | x | y = x } # y is nonlocal here
|
||||
setter1.call(99)
|
||||
puts y # prints 13
|
||||
setter2.call(77)
|
||||
puts y # prints 77
|
||||
|
||||
Note that although this proposal resembles declarations in JavaScript
|
||||
and Perl, the effect on the language is different because in those
|
||||
languages undeclared variables are global by default, whereas in
|
||||
Python undeclared variables are local by default. Thus, moving
|
||||
a function inside some other block in JavaScript or Perl can only
|
||||
reduce the scope of a previously global name reference, whereas in
|
||||
Python with this proposal, it could expand the scope of a previously
|
||||
local name reference.
|
||||
|
||||
Required Variable Declaration
|
||||
'''''''''''''''''''''''''''''
|
||||
|
||||
A more radical proposal [21]_ suggests removing Python's scope-guessing
|
||||
convention altogether and requiring that all names be declared in the
|
||||
scope where they are to be bound, much like Scheme. With this
|
||||
proposal, ``var x = 3`` would both declare ``x`` to belong to the
|
||||
local scope and bind it, where as ``x = 3`` would rebind the existing
|
||||
visible ``x``. In a context without an enclosing scope containing a
|
||||
``var x`` declaration, the statement ``x = 3`` would be statically
|
||||
determined to be illegal.
|
||||
|
||||
This proposal yields a simple and consistent model, but it would be
|
||||
incompatible with all existing Python code.
|
||||
|
||||
New Syntax in the Referring (Inner) Scope
|
||||
-----------------------------------------
|
||||
|
||||
There are three kinds of proposals in this category.
|
||||
|
||||
Outer Reference Expression
|
||||
''''''''''''''''''''''''''
|
||||
|
||||
This type of proposal suggests a new way of referring to a variable
|
||||
in an outer scope when using the variable in an expression. One
|
||||
syntax that has been suggested for this is ``.x`` [7]_, which would
|
||||
refer to ``x`` without creating a local binding for it. A concern
|
||||
with this proposal is that in many contexts ``x`` and ``.x`` could
|
||||
be used interchangeably, which would confuse the reader. A closely
|
||||
related idea is to use multiple dots to specify the number of scope
|
||||
levels to ascend [8]_, but most consider this too error-prone [17]_.
|
||||
|
||||
Rebinding Operator
|
||||
''''''''''''''''''
|
||||
|
||||
This proposal suggests a new assignment-like operator that rebinds
|
||||
a name without declaring the name to be local [2]_. Whereas the
|
||||
statement ``x = 3`` both declares ``x`` a local variable and binds
|
||||
it to 3, the statement ``x := 3`` would change the existing binding
|
||||
of ``x`` without declaring it local.
|
||||
|
||||
This is a simple solution, but according to PEP 3099 it has been
|
||||
rejected (perhaps because it would be too easy to miss or to confuse
|
||||
with ``=``).
|
||||
|
||||
Scope Override Declaration
|
||||
''''''''''''''''''''''''''
|
||||
|
||||
The proposals in this category suggest a new kind of declaration
|
||||
statement in the inner scope that prevents a name from becoming
|
||||
local. This statement would be similar in nature to the ``global``
|
||||
statement, but instead of making the name refer to a binding in the
|
||||
top module-level scope, it would make the name refer to the binding
|
||||
in the nearest enclosing scope.
|
||||
|
||||
This approach is attractive due to its parallel with a familiar
|
||||
Python construct, and because it retains context-independence for
|
||||
function definitions.
|
||||
|
||||
This approach also has advantages from a security and debugging
|
||||
perspective. The resulting Python would not only match the
|
||||
functionality of other nested-scope languages but would do so with a
|
||||
syntax that is arguably even better for defensive programming. In
|
||||
most other languages, a declaration contracts the scope of an
|
||||
existing name, so inadvertently omitting the declaration could yield
|
||||
farther-reaching (i.e. more dangerous) effects than expected. In
|
||||
Python with this proposal, the extra effort of adding the declaration
|
||||
is aligned with the increased risk of non-local effects (i.e. the
|
||||
path of least resistance is the safer path).
|
||||
|
||||
Many spellings have been suggested for such a declaration:
|
||||
|
||||
- ``scoped x`` [1]_
|
||||
- ``global x in f`` [3]_ (explicitly specify which scope)
|
||||
- ``free x`` [5]_
|
||||
- ``outer x`` [6]_
|
||||
- ``use x`` [9]_
|
||||
- ``global x`` [10]_ (change the meaning of ``global``)
|
||||
- ``nonlocal x`` [11]_
|
||||
- ``global x outer`` [18]_
|
||||
- ``global in x`` [18]_
|
||||
- ``not global x`` [18]_
|
||||
- ``extern x`` [20]_
|
||||
- ``ref x`` [22]_
|
||||
- ``refer x`` [22]_
|
||||
- ``share x`` [22]_
|
||||
- ``sharing x`` [22]_
|
||||
- ``common x`` [22]_
|
||||
- ``using x`` [22]_
|
||||
- ``borrow x`` [22]_
|
||||
- ``reuse x`` [23]_
|
||||
|
||||
The most commonly discussed choices appear to be ``outer``,
|
||||
``global``, and ``nonlocal``. ``outer`` is already used as both a
|
||||
variable name and an attribute name in the standard library. The
|
||||
word ``global`` has a conflicting meaning, because "global variable"
|
||||
is generally understood to mean a variable with top-level scope [24]_.
|
||||
In C, the keyword ``extern`` means that a name refers to a variable
|
||||
in a different compilation unit. While ``nonlocal`` is a bit long
|
||||
and less pleasant-sounding than some of the other options, it does
|
||||
have precisely the correct meaning: it declares a name not local.
|
||||
|
||||
|
||||
Proposed Solution
|
||||
=================
|
||||
|
||||
The solution proposed by this PEP is to add a scope override
|
||||
declaration in the referring (inner) scope. Guido has expressed a
|
||||
preference for this category of solution on Python-Dev [14]_ and has
|
||||
shown approval for ``nonlocal`` as the keyword [19]_.
|
||||
|
||||
The proposed declaration::
|
||||
|
||||
nonlocal x
|
||||
|
||||
prevents ``x`` from becoming a local name in the current scope. All
|
||||
occurrences of ``x`` in the current scope will refer to the ``x``
|
||||
bound in an outer enclosing scope. As with ``global``, multiple
|
||||
names are permitted::
|
||||
|
||||
nonlocal x, y, z
|
||||
|
||||
If there is no pre-existing binding in an enclosing scope, the
|
||||
compiler raises a SyntaxError. (It may be a bit of a stretch to
|
||||
call this a syntax error, but so far SyntaxError is used for all
|
||||
compile-time errors, including, for example, __future__ import
|
||||
with an unknown feature name.) Guido has said that this kind of
|
||||
declaration in the absence of an outer binding should be considered
|
||||
an error [16]_.
|
||||
|
||||
If a ``nonlocal`` declaration collides with the name of a formal
|
||||
parameter in the local scope, the compiler raises a SyntaxError.
|
||||
|
||||
A shorthand form is also permitted, in which ``nonlocal`` is
|
||||
prepended to an assignment or augmented assignment::
|
||||
|
||||
nonlocal x = 3
|
||||
|
||||
The above has exactly the same meaning as ``nonlocal x; x = 3``.
|
||||
(Guido supports a similar form of the ``global`` statement [26]_.)
|
||||
|
||||
On the left side of the shorthand form, only identifiers are allowed,
|
||||
not target expressions like ``x[0]``. Otherwise, all forms of
|
||||
assignment are allowed. The proposed grammar of the ``nonlocal``
|
||||
statement is::
|
||||
|
||||
nonlocal_stmt ::=
|
||||
"nonlocal" identifier ("," identifier)*
|
||||
["=" (target_list "=")+ expression_list]
|
||||
| "nonlocal" identifier augop expression_list
|
||||
|
||||
The rationale for allowing all these forms of assignment is that it
|
||||
simplifies understanding of the ``nonlocal`` statement. Separating
|
||||
the shorthand form into a declaration and an assignment is sufficient
|
||||
to understand what it means and whether it is valid.
|
||||
|
||||
|
||||
Backward Compatibility
|
||||
======================
|
||||
|
||||
This PEP targets Python 3000, as suggested by Guido [19]_. However,
|
||||
others have noted that some options considered in this PEP may be
|
||||
small enough changes to be feasible in Python 2.x [27]_, in which
|
||||
case this PEP could possibly be moved to be a 2.x series PEP.
|
||||
|
||||
As a (very rough) measure of the impact of introducing a new keyword,
|
||||
here is the number of times that some of the proposed keywords appear
|
||||
as identifiers in the standard library, according to a scan of the
|
||||
Python SVN repository on November 5, 2006::
|
||||
|
||||
nonlocal 0
|
||||
use 2
|
||||
using 3
|
||||
reuse 4
|
||||
free 8
|
||||
outer 147
|
||||
|
||||
``global`` appears 214 times as an existing keyword. As a measure
|
||||
of the impact of using ``global`` as the outer-scope keyword, there
|
||||
are 18 files in the standard library that would break as a result
|
||||
of such a change (because a function declares a variable ``global``
|
||||
before that variable has been introduced in the global scope)::
|
||||
|
||||
cgi.py
|
||||
dummy_thread.py
|
||||
mhlib.py
|
||||
mimetypes.py
|
||||
idlelib/PyShell.py
|
||||
idlelib/run.py
|
||||
msilib/__init__.py
|
||||
test/inspect_fodder.py
|
||||
test/test_compiler.py
|
||||
test/test_decimal.py
|
||||
test/test_descr.py
|
||||
test/test_dummy_threading.py
|
||||
test/test_fileinput.py
|
||||
test/test_global.py (not counted: this tests the keyword itself)
|
||||
test/test_grammar.py (not counted: this tests the keyword itself)
|
||||
test/test_itertools.py
|
||||
test/test_multifile.py
|
||||
test/test_scope.py (not counted: this tests the keyword itself)
|
||||
test/test_threaded_import.py
|
||||
test/test_threadsignals.py
|
||||
test/test_warnings.py
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] Scoping (was Re: Lambda binding solved?) (Rafael Bracho)
|
||||
http://www.python.org/search/hypermail/python-1994q1/0301.html
|
||||
|
||||
.. [2] Extended Function syntax (Just van Rossum)
|
||||
http://mail.python.org/pipermail/python-dev/2003-February/032764.html
|
||||
|
||||
.. [3] Closure semantics (Guido van Rossum)
|
||||
http://mail.python.org/pipermail/python-dev/2003-October/039214.html
|
||||
|
||||
.. [4] Better Control of Nested Lexical Scopes (Almann T. Goo)
|
||||
http://mail.python.org/pipermail/python-dev/2006-February/061568.html
|
||||
|
||||
.. [5] PEP for Better Control of Nested Lexical Scopes (Jeremy Hylton)
|
||||
http://mail.python.org/pipermail/python-dev/2006-February/061602.html
|
||||
|
||||
.. [6] PEP for Better Control of Nested Lexical Scopes (Almann T. Goo)
|
||||
http://mail.python.org/pipermail/python-dev/2006-February/061603.html
|
||||
|
||||
.. [7] Using and binding relative names (Phillip J. Eby)
|
||||
http://mail.python.org/pipermail/python-dev/2006-February/061636.html
|
||||
|
||||
.. [8] Using and binding relative names (Steven Bethard)
|
||||
http://mail.python.org/pipermail/python-dev/2006-February/061749.html
|
||||
|
||||
.. [9] Lexical scoping in Python 3k (Ka-Ping Yee)
|
||||
http://mail.python.org/pipermail/python-dev/2006-July/066862.html
|
||||
|
||||
.. [10] Lexical scoping in Python 3k (Greg Ewing)
|
||||
http://mail.python.org/pipermail/python-dev/2006-July/066889.html
|
||||
|
||||
.. [11] Lexical scoping in Python 3k (Ka-Ping Yee)
|
||||
http://mail.python.org/pipermail/python-dev/2006-July/066942.html
|
||||
|
||||
.. [12] Lexical scoping in Python 3k (Guido van Rossum)
|
||||
http://mail.python.org/pipermail/python-dev/2006-July/066950.html
|
||||
|
||||
.. [13] Explicit Lexical Scoping (pre-PEP?) (Talin)
|
||||
http://mail.python.org/pipermail/python-dev/2006-July/066978.html
|
||||
|
||||
.. [14] Explicit Lexical Scoping (pre-PEP?) (Guido van Rossum)
|
||||
http://mail.python.org/pipermail/python-dev/2006-July/066991.html
|
||||
|
||||
.. [15] Explicit Lexical Scoping (pre-PEP?) (Guido van Rossum)
|
||||
http://mail.python.org/pipermail/python-dev/2006-July/066995.html
|
||||
|
||||
.. [16] Lexical scoping in Python 3k (Guido van Rossum)
|
||||
http://mail.python.org/pipermail/python-dev/2006-July/066968.html
|
||||
|
||||
.. [17] Explicit Lexical Scoping (pre-PEP?) (Guido van Rossum)
|
||||
http://mail.python.org/pipermail/python-dev/2006-July/067004.html
|
||||
|
||||
.. [18] Explicit Lexical Scoping (pre-PEP?) (Andrew Clover)
|
||||
http://mail.python.org/pipermail/python-dev/2006-July/067007.html
|
||||
|
||||
.. [19] Explicit Lexical Scoping (pre-PEP?) (Guido van Rossum)
|
||||
http://mail.python.org/pipermail/python-dev/2006-July/067067.html
|
||||
|
||||
.. [20] Explicit Lexical Scoping (pre-PEP?) (Matthew Barnes)
|
||||
http://mail.python.org/pipermail/python-dev/2006-July/067221.html
|
||||
|
||||
.. [21] Sky pie: a "var" keyword (a thread started by Neil Toronto)
|
||||
http://mail.python.org/pipermail/python-3000/2006-October/003968.html
|
||||
|
||||
.. [22] Alternatives to 'outer' (Talin)
|
||||
http://mail.python.org/pipermail/python-3000/2006-October/004021.html
|
||||
|
||||
.. [23] Alternatives to 'outer' (Jim Jewett)
|
||||
http://mail.python.org/pipermail/python-3000/2006-November/004153.html
|
||||
|
||||
.. [24] Global variable (version 2006-11-01T01:23:16)
|
||||
http://en.wikipedia.org/wiki/Global_variable
|
||||
|
||||
.. [25] Ruby 2.0 block local variable
|
||||
http://redhanded.hobix.com/inspect/ruby20BlockLocalVariable.html
|
||||
|
||||
.. [26] Draft PEP for outer scopes (Guido van Rossum)
|
||||
http://mail.python.org/pipermail/python-3000/2006-November/004166.html
|
||||
|
||||
.. [27] Draft PEP for outer scopes (Nick Coghlan)
|
||||
http://mail.python.org/pipermail/python-3000/2006-November/004237.html
|
||||
|
||||
Acknowledgements
|
||||
================
|
||||
|
||||
The ideas and proposals mentioned in this PEP are gleaned from
|
||||
countless Python-Dev postings. Thanks to Jim Jewett, Mike Orr,
|
||||
Jason Orendorff, and Christian Tanzer for suggesting specific
|
||||
edits to this PEP.
|
||||
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
This document has been placed in the public domain.
|
||||
|
||||
|
||||
..
|
||||
Local Variables:
|
||||
mode: indented-text
|
||||
indent-tabs-mode: nil
|
||||
sentence-end-double-space: t
|
||||
fill-column: 70
|
||||
coding: utf-8
|
||||
End:
|
Loading…
Reference in New Issue