Convert 10 PEPs to reSt (#180)

This commit is contained in:
Mariatta 2017-01-19 10:00:30 -08:00 committed by Brett Cannon
parent fdc405df22
commit 87dc92a34e
10 changed files with 1198 additions and 1042 deletions

View File

@ -5,163 +5,180 @@ Last-Modified: $Date$
Author: nowonder@nowonder.de (Peter Schneider-Kamp)
Status: Deferred
Type: Standards Track
Content-Type: text/x-rst
Created: 22-Aug-2000
Python-Version: 2.1
Post-History:
Introduction
============
This PEP describes the often proposed feature of exposing the loop
counter in for-loops. This PEP tracks the status and ownership of
this feature. It contains a description of the feature and
outlines changes necessary to support the feature. This PEP
summarizes discussions held in mailing list forums, and provides
URLs for further information, where appropriate. The CVS revision
history of this file contains the definitive historical record.
This PEP describes the often proposed feature of exposing the loop
counter in for-loops. This PEP tracks the status and ownership of
this feature. It contains a description of the feature and
outlines changes necessary to support the feature. This PEP
summarizes discussions held in mailing list forums, and provides
URLs for further information, where appropriate. The CVS revision
history of this file contains the definitive historical record.
Motivation
==========
Standard for-loops in Python iterate over the elements of a
sequence[1]. Often it is desirable to loop over the indices or
both the elements and the indices instead.
Standard for-loops in Python iterate over the elements of a
sequence [1]_. Often it is desirable to loop over the indices or
both the elements and the indices instead.
The common idioms used to accomplish this are unintuitive. This
PEP proposes two different ways of exposing the indices.
The common idioms used to accomplish this are unintuitive. This
PEP proposes two different ways of exposing the indices.
Loop counter iteration
======================
The current idiom for looping over the indices makes use of the
built-in 'range' function:
The current idiom for looping over the indices makes use of the
built-in ``range`` function::
for i in range(len(sequence)):
# work with index i
for i in range(len(sequence)):
# work with index i
Looping over both elements and indices can be achieved either by the
old idiom or by using the new 'zip' built-in function[2]:
Looping over both elements and indices can be achieved either by the
old idiom or by using the new ``zip`` built-in function [2]_::
for i in range(len(sequence)):
e = sequence[i]
# work with index i and element e
for i in range(len(sequence)):
e = sequence[i]
# work with index i and element e
or
or::
for i, e in zip(range(len(sequence)), sequence):
# work with index i and element e
for i, e in zip(range(len(sequence)), sequence):
# work with index i and element e
The Proposed Solutions
======================
There are three solutions that have been discussed. One adds a
non-reserved keyword, the other adds two built-in functions.
A third solution adds methods to sequence objects.
There are three solutions that have been discussed. One adds a
non-reserved keyword, the other adds two built-in functions.
A third solution adds methods to sequence objects.
Non-reserved keyword 'indexing'
Non-reserved keyword ``indexing``
=================================
This solution would extend the syntax of the for-loop by adding
an optional '<variable> indexing' clause which can also be used
instead of the '<variable> in' clause..
This solution would extend the syntax of the for-loop by adding
an optional ``<variable> indexing`` clause which can also be used
instead of the ``<variable> in`` clause.
Looping over the indices of a sequence would thus become:
Looping over the indices of a sequence would thus become::
for i indexing sequence:
# work with index i
for i indexing sequence:
# work with index i
Looping over both indices and elements would similarly be:
Looping over both indices and elements would similarly be::
for i indexing e in sequence:
# work with index i and element e
for i indexing e in sequence:
# work with index i and element e
Built-in functions 'indices' and 'irange'
Built-in functions ``indices`` and ``irange``
=============================================
This solution adds two built-in functions 'indices' and 'irange'.
The semantics of these can be described as follows:
This solution adds two built-in functions ``indices`` and ``irange``.
The semantics of these can be described as follows::
def indices(sequence):
return range(len(sequence))
def indices(sequence):
return range(len(sequence))
def irange(sequence):
return zip(range(len(sequence)), sequence)
def irange(sequence):
return zip(range(len(sequence)), sequence)
These functions could be implemented either eagerly or lazily and
should be easy to extend in order to accept more than one sequence
argument.
These functions could be implemented either eagerly or lazily and
should be easy to extend in order to accept more than one sequence
argument.
The use of these functions would simplify the idioms for looping
over the indices and over both elements and indices:
The use of these functions would simplify the idioms for looping
over the indices and over both elements and indices::
for i in indices(sequence):
# work with index i
for i in indices(sequence):
# work with index i
for i, e in irange(sequence):
# work with index i and element e
for i, e in irange(sequence):
# work with index i and element e
Methods for sequence objects
============================
This solution proposes the addition of 'indices', 'items'
and 'values' methods to sequences, which enable looping over
indices only, both indices and elements, and elements only
respectively.
This solution proposes the addition of ``indices``, ``items``
and ``values`` methods to sequences, which enable looping over
indices only, both indices and elements, and elements only
respectively.
This would immensely simplify the idioms for looping over indices
and for looping over both elements and indices:
This would immensely simplify the idioms for looping over indices
and for looping over both elements and indices::
for i in sequence.indices():
# work with index i
for i in sequence.indices():
# work with index i
for i, e in sequence.items():
# work with index i and element e
for i, e in sequence.items():
# work with index i and element e
Additionally it would allow to do looping over the elements
of sequences and dicitionaries in a consistent way:
Additionally it would allow to do looping over the elements
of sequences and dictionaries in a consistent way::
for e in sequence_or_dict.values():
# do something with element e
for e in sequence_or_dict.values():
# do something with element e
Implementations
===============
For all three solutions some more or less rough patches exist
as patches at SourceForge:
For all three solutions some more or less rough patches exist
as patches at SourceForge:
'for i indexing a in l': exposing the for-loop counter[3]
add indices() and irange() to built-ins[4]
add items() method to listobject[5]
- ``for i indexing a in l``: exposing the for-loop counter [3]_
- add ``indices()`` and ``irange()`` to built-ins [4]_
- add ``items()`` method to listobject [5]_
All of them have been pronounced on and rejected by the BDFL.
All of them have been pronounced on and rejected by the BDFL.
Note that the 'indexing' keyword is only a NAME in the
grammar and so does not hinder the general use of 'indexing'.
Note that the ``indexing`` keyword is only a ``NAME`` in the
grammar and so does not hinder the general use of ``indexing``.
Backward Compatibility Issues
=============================
As no keywords are added and the semantics of existing code
remains unchanged, all three solutions can be implemented
without breaking existing code.
As no keywords are added and the semantics of existing code
remains unchanged, all three solutions can be implemented
without breaking existing code.
Copyright
=========
This document has been placed in the public domain.
This document has been placed in the public domain.
References
==========
[1] http://docs.python.org/reference/compound_stmts.html#for
[2] Lockstep Iteration, PEP 201
[3] http://sourceforge.net/patch/download.php?id=101138
[4] http://sourceforge.net/patch/download.php?id=101129
[5] http://sourceforge.net/patch/download.php?id=101178
.. [1] http://docs.python.org/reference/compound_stmts.html#for
.. [2] Lockstep Iteration, PEP 201
.. [3] http://sourceforge.net/patch/download.php?id=101138
.. [4] http://sourceforge.net/patch/download.php?id=101129
.. [5] http://sourceforge.net/patch/download.php?id=101178
Local Variables:
mode: indented-text
indent-tabs-mode: nil
End:
..
Local Variables:
mode: indented-text
indent-tabs-mode: nil
End:

View File

@ -5,139 +5,164 @@ Last-Modified: $Date$
Author: moshez@zadka.site.co.il (Moshe Zadka)
Status: Rejected
Type: Informational
Content-Type: text/x-rst
Created: 31-Jul-2000
Post-History:
Superseded-By: 287
Notice
This PEP is rejected by the author. It has been superseded by PEP
287.
Notice
======
This PEP is rejected by the author. It has been superseded by PEP
287.
Abstract
========
Named Python objects, such as modules, classes and functions, have a
string attribute called __doc__. If the first expression inside
the definition is a literal string, that string is assigned
to the __doc__ attribute.
Named Python objects, such as modules, classes and functions, have a
string attribute called ``__doc__``. If the first expression inside
the definition is a literal string, that string is assigned
to the ``__doc__`` attribute.
The ``__doc__`` attribute is called a documentation string, or docstring.
It is often used to summarize the interface of the module, class or
function. However, since there is no common format for documentation
string, tools for extracting docstrings and transforming those into
documentation in a standard format (e.g., DocBook) have not sprang
up in abundance, and those that do exist are for the most part
unmaintained and unused.
The __doc__ attribute is called a documentation string, or docstring.
It is often used to summarize the interface of the module, class or
function. However, since there is no common format for documentation
string, tools for extracting docstrings and transforming those into
documentation in a standard format (e.g., DocBook) have not sprang
up in abundance, and those that do exist are for the most part
unmaintained and unused.
Perl Documentation
==================
In Perl, most modules are documented in a format called POD -- Plain
Old Documentation. This is an easy-to-type, very low level format
which integrates well with the Perl parser. Many tools exist to turn
POD documentation into other formats: info, HTML and man pages, among
others. However, in Perl, the information is not available at run-time.
In Perl, most modules are documented in a format called POD -- Plain
Old Documentation. This is an easy-to-type, very low level format
which integrates well with the Perl parser. Many tools exist to turn
POD documentation into other formats: info, HTML and man pages, among
others. However, in Perl, the information is not available at run-time.
Java Documentation
==================
In Java, special comments before classes and functions function to
document the code. A program to extract these, and turn them into
HTML documentation is called javadoc, and is part of the standard
Java distribution. However, the only output format that is supported
is HTML, and JavaDoc has a very intimate relationship with HTML.
In Java, special comments before classes and functions function to
document the code. A program to extract these, and turn them into
HTML documentation is called javadoc, and is part of the standard
Java distribution. However, the only output format that is supported
is HTML, and JavaDoc has a very intimate relationship with HTML.
Python Docstring Goals
======================
Python documentation string are easy to spot during parsing, and are
also available to the runtime interpreter. This double purpose is
a bit problematic, sometimes: for example, some are reluctant to have
too long docstrings, because they do not want to take much space in
the runtime. In addition, because of the current lack of tools, people
read objects' docstrings by "print"ing them, so a tendency to make them
brief and free of markups has sprung up. This tendency hinders writing
better documentation-extraction tools, since it causes docstrings to
contain little information, which is hard to parse.
Python documentation string are easy to spot during parsing, and are
also available to the runtime interpreter. This double purpose is
a bit problematic, sometimes: for example, some are reluctant to have
too long docstrings, because they do not want to take much space in
the runtime. In addition, because of the current lack of tools, people
read objects' docstrings by "print"ing them, so a tendency to make them
brief and free of markups has sprung up. This tendency hinders writing
better documentation-extraction tools, since it causes docstrings to
contain little information, which is hard to parse.
High Level Solutions
====================
To counter the objection that the strings take up place in the running
program, it is suggested that documentation extraction tools will
concatenate a maximum prefix of string literals which appear in the
beginning of a definition. The first of these will also be available
in the interactive interpreter, so it should contain a few summary
lines.
To counter the objection that the strings take up place in the running
program, it is suggested that documentation extraction tools will
concatenate a maximum prefix of string literals which appear in the
beginning of a definition. The first of these will also be available
in the interactive interpreter, so it should contain a few summary
lines.
Docstring Format Goals
======================
These are the goals for the docstring format, as discussed ad neasum
in the doc-sig.
These are the goals for the docstring format, as discussed ad nauseam
in the doc-sig.
1. It must be easy to type with any standard text editor.
2. It must be readable to the casual observer.
3. It must not contain information which can be deduced from parsing
the module.
4. It must contain sufficient information so it can be converted
to any reasonable markup format.
5. It must be possible to write a module's entire documentation in
docstrings, without feeling hampered by the markup language.
1. It must be easy to type with any standard text editor.
2. It must be readable to the casual observer.
3. It must not contain information which can be deduced from parsing
the module.
4. It must contain sufficient information so it can be converted
to any reasonable markup format.
5. It must be possible to write a module's entire documentation in
docstrings, without feeling hampered by the markup language.
Docstring Contents
==================
For requirement 5. above, it is needed to specify what must be
in docstrings.
For requirement 5. above, it is needed to specify what must be
in docstrings.
At least the following must be available:
At least the following must be available:
a. A tag that means "this is a Python ``something'', guess what"
a. A tag that means "this is a Python `something`, guess what"
Example: In the sentence "The POP3 class", we need to markup "POP3"
so. The parser will be able to guess it is a class from the contents
of the poplib module, but we need to make it guess.
Example: In the sentence "The POP3 class", we need to markup "POP3"
so. The parser will be able to guess it is a class from the contents
of the ``poplib`` module, but we need to make it guess.
b. Tags that mean "this is a Python class/module/class var/instance var..."
b. Tags that mean "this is a Python class/module/class var/instance var..."
Example: The usual Python idiom for singleton class A is to have _A
as the class, and A a function which returns _A objects. It's usual
to document the class, nonetheless, as being A. This requires the
strength to say "The class A" and have A hyperlinked and marked-up
as a class.
Example: The usual Python idiom for singleton class ``A`` is to have ``_A``
as the class, and ``A`` a function which returns ``_A`` objects. It's usual
to document the class, nonetheless, as being ``A``. This requires the
strength to say "The class ``A``" and have ``A`` hyperlinked and marked-up
as a class.
c. An easy way to include Python source code/Python interactive sessions
d. Emphasis/bold
e. List/tables
c. An easy way to include Python source code/Python interactive sessions
d. Emphasis/bold
e. List/tables
Docstring Basic Structure
=========================
The documentation strings will be in StructuredTextNG
(http://www.zope.org/Members/jim/StructuredTextWiki/StructuredTextNG)
Since StructuredText is not yet strong enough to handle (a) and (b)
above, we will need to extend it. I suggest using
``[<optional description>:python identifier]``.
E.g.: ``[class:POP3]``, ``[:POP3.list]``, etc. If the description is missing,
a guess will be made from the text.
The documentation strings will be in StructuredTextNG
(http://www.zope.org/Members/jim/StructuredTextWiki/StructuredTextNG)
Since StructuredText is not yet strong enough to handle (a) and (b)
above, we will need to extend it. I suggest using
'[<optional description>:python identifier]'.
E.g.: [class:POP3], [:POP3.list], etc. If the description is missing,
a guess will be made from the text.
Unresolved Issues
Is there a way to escape characters in ST? If so, how?
(example: * at the beginning of a line without being bullet symbol)
=================
Is my suggestion above for Python symbols compatible with ST-NG?
How hard would it be to extend ST-NG to support it?
Is there a way to escape characters in ST? If so, how?
(example: * at the beginning of a line without being bullet symbol)
How do we describe input and output types of functions?
Is my suggestion above for Python symbols compatible with ST-NG?
How hard would it be to extend ST-NG to support it?
What additional constraint do we enforce on each docstring?
(module/class/function)?
How do we describe input and output types of functions?
What additional constraint do we enforce on each docstring?
(module/class/function)?
What are the guesser rules?
What are the guesser rules?
Rejected Suggestions
====================
XML -- it's very hard to type, and too cluttered to read it
comfortably.
XML -- it's very hard to type, and too cluttered to read it comfortably.
Local Variables:
mode: indented-text
indent-tabs-mode: nil
End:
..
Local Variables:
mode: indented-text
indent-tabs-mode: nil
End:

View File

@ -5,187 +5,195 @@ Last-Modified: $Date$
Author: gmcm@hypernet.com (Gordon McMillan)
Status: Deferred
Type: Standards Track
Content-Type: text/x-rst
Created: 14-Aug-2000
Python-Version: 2.1
Post-History:
Introduction
============
This PEP discusses changes required to core Python in order to
efficiently support generators, microthreads and coroutines. It is
related to PEP 220, which describes how Python should be extended
to support these facilities. The focus of this PEP is strictly on
the changes required to allow these extensions to work.
This PEP discusses changes required to core Python in order to
efficiently support generators, microthreads and coroutines. It is
related to PEP 220, which describes how Python should be extended
to support these facilities. The focus of this PEP is strictly on
the changes required to allow these extensions to work.
While these PEPs are based on Christian Tismer's Stackless[1]
implementation, they do not regard Stackless as a reference
implementation. Stackless (with an extension module) implements
continuations, and from continuations one can implement
coroutines, microthreads (as has been done by Will Ware[2]) and
generators. But in more that a year, no one has found any other
productive use of continuations, so there seems to be no demand
for their support.
While these PEPs are based on Christian Tismer's Stackless [1]_
implementation, they do not regard Stackless as a reference
implementation. Stackless (with an extension module) implements
continuations, and from continuations one can implement
coroutines, microthreads (as has been done by Will Ware [2]_) and
generators. But in more that a year, no one has found any other
productive use of continuations, so there seems to be no demand
for their support.
However, Stackless support for continuations is a relatively minor
piece of the implementation, so one might regard it as "a"
reference implementation (rather than "the" reference
implementation).
However, Stackless support for continuations is a relatively minor
piece of the implementation, so one might regard it as "a"
reference implementation (rather than "the" reference
implementation).
Background
==========
Generators and coroutines have been implemented in a number of
languages in a number of ways. Indeed, Tim Peters has done pure
Python implementations of generators[3] and coroutines[4] using
threads (and a thread-based coroutine implementation exists for
Java). However, the horrendous overhead of a thread-based
implementation severely limits the usefulness of this approach.
Generators and coroutines have been implemented in a number of
languages in a number of ways. Indeed, Tim Peters has done pure
Python implementations of generators [3]_ and coroutines [4]_ using
threads (and a thread-based coroutine implementation exists for
Java). However, the horrendous overhead of a thread-based
implementation severely limits the usefulness of this approach.
Microthreads (a.k.a "green" or "user" threads) and coroutines
involve transfers of control that are difficult to accommodate in
a language implementation based on a single stack. (Generators can
be done on a single stack, but they can also be regarded as a very
simple case of coroutines.)
Microthreads (a.k.a "green" or "user" threads) and coroutines
involve transfers of control that are difficult to accommodate in
a language implementation based on a single stack. (Generators can
be done on a single stack, but they can also be regarded as a very
simple case of coroutines.)
Real threads allocate a full-sized stack for each thread of
control, and this is the major source of overhead. However,
coroutines and microthreads can be implemented in Python in a way
that involves almost no overhead. This PEP, therefor, offers a
way for making Python able to realistically manage thousands of
separate "threads" of activity (vs. today's limit of perhaps dozens
of separate threads of activity).
Real threads allocate a full-sized stack for each thread of
control, and this is the major source of overhead. However,
coroutines and microthreads can be implemented in Python in a way
that involves almost no overhead. This PEP, therefor, offers a
way for making Python able to realistically manage thousands of
separate "threads" of activity (vs. today's limit of perhaps dozens
of separate threads of activity).
Another justification for this PEP (explored in PEP 220) is that
coroutines and generators often allow a more direct expression of
an algorithm than is possible in today's Python.
Another justification for this PEP (explored in PEP 220) is that
coroutines and generators often allow a more direct expression of
an algorithm than is possible in today's Python.
Discussion
==========
The first thing to note is that Python, while it mingles
interpreter data (normal C stack usage) with Python data (the
state of the interpreted program) on the stack, the two are
logically separate. They just happen to use the same stack.
The first thing to note is that Python, while it mingles
interpreter data (normal C stack usage) with Python data (the
state of the interpreted program) on the stack, the two are
logically separate. They just happen to use the same stack.
A real thread gets something approaching a process-sized stack
because the implementation has no way of knowing how much stack
space the thread will require. The stack space required for an
individual frame is likely to be reasonable, but stack switching
is an arcane and non-portable process, not supported by C.
A real thread gets something approaching a process-sized stack
because the implementation has no way of knowing how much stack
space the thread will require. The stack space required for an
individual frame is likely to be reasonable, but stack switching
is an arcane and non-portable process, not supported by C.
Once Python stops putting Python data on the C stack, however,
stack switching becomes easy.
Once Python stops putting Python data on the C stack, however,
stack switching becomes easy.
The fundamental approach of the PEP is based on these two
ideas. First, separate C's stack usage from Python's stack
usage. Secondly, associate with each frame enough stack space to
handle that frame's execution.
The fundamental approach of the PEP is based on these two
ideas. First, separate C's stack usage from Python's stack
usage. Secondly, associate with each frame enough stack space to
handle that frame's execution.
In the normal usage, Stackless Python has a normal stack
structure, except that it is broken into chunks. But in the
presence of a coroutine / microthread extension, this same
mechanism supports a stack with a tree structure. That is, an
extension can support transfers of control between frames outside
the normal "call / return" path.
In the normal usage, Stackless Python has a normal stack
structure, except that it is broken into chunks. But in the
presence of a coroutine / microthread extension, this same
mechanism supports a stack with a tree structure. That is, an
extension can support transfers of control between frames outside
the normal "call / return" path.
Problems
========
The major difficulty with this approach is C calling Python. The
problem is that the C stack now holds a nested execution of the
byte-code interpreter. In that situation, a coroutine /
microthread extension cannot be permitted to transfer control to a
frame in a different invocation of the byte-code interpreter. If a
frame were to complete and exit back to C from the wrong
interpreter, the C stack could be trashed.
The major difficulty with this approach is C calling Python. The
problem is that the C stack now holds a nested execution of the
byte-code interpreter. In that situation, a coroutine /
microthread extension cannot be permitted to transfer control to a
frame in a different invocation of the byte-code interpreter. If a
frame were to complete and exit back to C from the wrong
interpreter, the C stack could be trashed.
The ideal solution is to create a mechanism where nested
executions of the byte code interpreter are never needed. The easy
solution is for the coroutine / microthread extension(s) to
recognize the situation and refuse to allow transfers outside the
current invocation.
The ideal solution is to create a mechanism where nested
executions of the byte code interpreter are never needed. The easy
solution is for the coroutine / microthread extension(s) to
recognize the situation and refuse to allow transfers outside the
current invocation.
We can categorize code that involves C calling Python into two
camps: Python's implementation, and C extensions. And hopefully we
can offer a compromise: Python's internal usage (and C extension
writers who want to go to the effort) will no longer use a nested
invocation of the interpreter. Extensions which do not go to the
effort will still be safe, but will not play well with coroutines
/ microthreads.
We can categorize code that involves C calling Python into two
camps: Python's implementation, and C extensions. And hopefully we
can offer a compromise: Python's internal usage (and C extension
writers who want to go to the effort) will no longer use a nested
invocation of the interpreter. Extensions which do not go to the
effort will still be safe, but will not play well with coroutines
/ microthreads.
Generally, when a recursive call is transformed into a loop, a bit
of extra bookkeeping is required. The loop will need to keep its
own "stack" of arguments and results since the real stack can now
only hold the most recent. The code will be more verbose, because
it's not quite as obvious when we're done. While Stackless is not
implemented this way, it has to deal with the same issues.
Generally, when a recursive call is transformed into a loop, a bit
of extra bookkeeping is required. The loop will need to keep its
own "stack" of arguments and results since the real stack can now
only hold the most recent. The code will be more verbose, because
it's not quite as obvious when we're done. While Stackless is not
implemented this way, it has to deal with the same issues.
In normal Python, PyEval_EvalCode is used to build a frame and
execute it. Stackless Python introduces the concept of a
FrameDispatcher. Like PyEval_EvalCode, it executes one frame. But
the interpreter may signal the FrameDispatcher that a new frame
has been swapped in, and the new frame should be executed. When a
frame completes, the FrameDispatcher follows the back pointer to
resume the "calling" frame.
In normal Python, ``PyEval_EvalCode`` is used to build a frame and
execute it. Stackless Python introduces the concept of a
``FrameDispatcher``. Like ``PyEval_EvalCode``, it executes one frame. But
the interpreter may signal the ``FrameDispatcher`` that a new frame
has been swapped in, and the new frame should be executed. When a
frame completes, the ``FrameDispatcher`` follows the back pointer to
resume the "calling" frame.
So Stackless transforms recursions into a loop, but it is not the
FrameDispatcher that manages the frames. This is done by the
interpreter (or an extension that knows what it's doing).
So Stackless transforms recursions into a loop, but it is not the
``FrameDispatcher`` that manages the frames. This is done by the
interpreter (or an extension that knows what it's doing).
The general idea is that where C code needs to execute Python
code, it creates a frame for the Python code, setting its back
pointer to the current frame. Then it swaps in the frame, signals
the FrameDispatcher and gets out of the way. The C stack is now
clean - the Python code can transfer control to any other frame
(if an extension gives it the means to do so).
The general idea is that where C code needs to execute Python
code, it creates a frame for the Python code, setting its back
pointer to the current frame. Then it swaps in the frame, signals
the ``FrameDispatcher`` and gets out of the way. The C stack is now
clean - the Python code can transfer control to any other frame
(if an extension gives it the means to do so).
In the vanilla case, this magic can be hidden from the programmer
(even, in most cases, from the Python-internals programmer). Many
situations present another level of difficulty, however.
In the vanilla case, this magic can be hidden from the programmer
(even, in most cases, from the Python-internals programmer). Many
situations present another level of difficulty, however.
The map builtin function involves two obstacles to this
approach. It cannot simply construct a frame and get out of the
way, not just because there's a loop involved, but each pass
through the loop requires some "post" processing. In order to play
well with others, Stackless constructs a frame object for map
itself.
The map builtin function involves two obstacles to this
approach. It cannot simply construct a frame and get out of the
way, not just because there's a loop involved, but each pass
through the loop requires some "post" processing. In order to play
well with others, Stackless constructs a frame object for map
itself.
Most recursions of the interpreter are not this complex, but
fairly frequently, some "post" operations are required. Stackless
does not fix these situations because of amount of code changes
required. Instead, Stackless prohibits transfers out of a nested
interpreter. While not ideal (and sometimes puzzling), this
limitation is hardly crippling.
Most recursions of the interpreter are not this complex, but
fairly frequently, some "post" operations are required. Stackless
does not fix these situations because of amount of code changes
required. Instead, Stackless prohibits transfers out of a nested
interpreter. While not ideal (and sometimes puzzling), this
limitation is hardly crippling.
Advantages
==========
For normal Python, the advantage to this approach is that C stack
usage becomes much smaller and more predictable. Unbounded
recursion in Python code becomes a memory error, instead of a
stack error (and thus, in non-Cupertino operating systems,
something that can be recovered from). The price, of course, is
the added complexity that comes from transforming recursions of
the byte-code interpreter loop into a higher order loop (and the
attendant bookkeeping involved).
For normal Python, the advantage to this approach is that C stack
usage becomes much smaller and more predictable. Unbounded
recursion in Python code becomes a memory error, instead of a
stack error (and thus, in non-Cupertino operating systems,
something that can be recovered from). The price, of course, is
the added complexity that comes from transforming recursions of
the byte-code interpreter loop into a higher order loop (and the
attendant bookkeeping involved).
The big advantage comes from realizing that the Python stack is
really a tree, and the frame dispatcher can transfer control
freely between leaf nodes of the tree, thus allowing things like
microthreads and coroutines.
The big advantage comes from realizing that the Python stack is
really a tree, and the frame dispatcher can transfer control
freely between leaf nodes of the tree, thus allowing things like
microthreads and coroutines.
References
==========
[1] http://www.stackless.com
[2] http://world.std.com/~wware/uthread.html
[3] Demo/threads/Generator.py in the source distribution
[4] http://www.stackless.com/coroutines.tim.peters.html
.. [1] http://www.stackless.com
.. [2] http://web.archive.org/web/20000815070602/http://world.std.com/~wware/uthread.html
.. [3] Demo/threads/Generator.py in the source distribution
.. [4] http://www.stackless.com/coroutines.tim.peters.html
Local Variables:
mode: indented-text
indent-tabs-mode: nil
End:
..
Local Variables:
mode: indented-text
indent-tabs-mode: nil
End:

View File

@ -5,140 +5,152 @@ Last-Modified: $Date$
Author: moshez@zadka.site.co.il (Moshe Zadka), guido@python.org (Guido van Rossum)
Status: Withdrawn
Type: Standards Track
Content-Type: text/x-rst
Created: 4-Nov-2000
Python-Version: ??
Post-History:
Withdrawal
==========
This PEP has been withdrawn in favor of PEP 3141.
This PEP has been withdrawn in favor of PEP 3141.
Abstract
========
Today, Python's numerical model is similar to the C numeric model:
there are several unrelated numerical types, and when operations
between numerical types are requested, coercions happen. While
the C rationale for the numerical model is that it is very similar
to what happens at the hardware level, that rationale does not
apply to Python. So, while it is acceptable to C programmers that
2/3 == 0, it is surprising to many Python programmers.
Today, Python's numerical model is similar to the C numeric model:
there are several unrelated numerical types, and when operations
between numerical types are requested, coercions happen. While
the C rationale for the numerical model is that it is very similar
to what happens at the hardware level, that rationale does not
apply to Python. So, while it is acceptable to C programmers that
2/3 == 0, it is surprising to many Python programmers.
NOTE: in the light of recent discussions in the newsgroup, the
motivation in this PEP (and details) need to be extended.
NOTE: in the light of recent discussions in the newsgroup, the
motivation in this PEP (and details) need to be extended.
Rationale
=========
In usability studies, one of the least usable aspect of Python was
the fact that integer division returns the floor of the division.
This makes it hard to program correctly, requiring casts to
float() in various parts through the code. Python's numerical
model stems from C, while a model that might be easier to work with
can be based on the mathematical understanding of numbers.
In usability studies, one of the least usable aspect of Python was
the fact that integer division returns the floor of the division.
This makes it hard to program correctly, requiring casts to
``float()`` in various parts through the code. Python's numerical
model stems from C, while a model that might be easier to work with
can be based on the mathematical understanding of numbers.
Other Numerical Models
======================
Perl's numerical model is that there is one type of numbers --
floating point numbers. While it is consistent and superficially
non-surprising, it tends to have subtle gotchas. One of these is
that printing numbers is very tricky, and requires correct
rounding. In Perl, there is also a mode where all numbers are
integers. This mode also has its share of problems, which arise
from the fact that there is not even an approximate way of
dividing numbers and getting meaningful answers.
Perl's numerical model is that there is one type of numbers --
floating point numbers. While it is consistent and superficially
non-surprising, it tends to have subtle gotchas. One of these is
that printing numbers is very tricky, and requires correct
rounding. In Perl, there is also a mode where all numbers are
integers. This mode also has its share of problems, which arise
from the fact that there is not even an approximate way of
dividing numbers and getting meaningful answers.
Suggested Interface For Python's Numerical Model
================================================
While coercion rules will remain for add-on types and classes, the
built in type system will have exactly one Python type -- a
number. There are several things which can be considered "number
methods":
While coercion rules will remain for add-on types and classes, the
built in type system will have exactly one Python type -- a
number. There are several things which can be considered "number
methods":
1. isnatural()
2. isintegral()
3. isrational()
4. isreal()
5. iscomplex()
1. ``isnatural()``
2. ``isintegral()``
3. ``isrational()``
4. ``isreal()``
5. ``iscomplex()``
6. ``isexact()``
a. isexact()
Obviously, a number which answers true to a question from 1 to 5, will
also answer true to any following question. If ``isexact()`` is not true,
then any answer might be wrong.
(But not horribly wrong: it's close to the truth.)
Obviously, a number which answers true to a question from 1 to 5, will
also answer true to any following question. If "isexact()" is not true,
then any answer might be wrong.
(But not horribly wrong: it's close to the truth.)
Now, there is two thing the models promises for the field operations
(+, -, /, \*):
Now, there is two thing the models promises for the field operations
(+, -, /, *):
- If both operands satisfy ``isexact()``, the result satisfies
``isexact()``.
- If both operands satisfy isexact(), the result satisfies
isexact().
- All field rules are true, except that for not-``isexact()`` numbers,
they might be only approximately true.
- All field rules are true, except that for not-isexact() numbers,
they might be only approximately true.
One consequence of these two rules is that all exact calcutions
are done as (complex) rationals: since the field laws must hold,
then::
One consequence of these two rules is that all exact calcutions
are done as (complex) rationals: since the field laws must hold,
then
(a/b)*b == a
(a/b)*b == a
must hold.
must hold.
There is built-in function, ``inexact()`` which takes a number
and returns an inexact number which is a good approximation.
Inexact numbers must be as least as accurate as if they were
using IEEE-754.
There is built-in function, inexact() which takes a number
and returns an inexact number which is a good approximation.
Inexact numbers must be as least as accurate as if they were
using IEEE-754.
Several of the classical Python functions will return exact numbers
even when given inexact numbers: e.g, ``int()``.
Several of the classical Python functions will return exact numbers
even when given inexact numbers: e.g, int().
Coercion
========
The number type does not define ``nb_coerce``
Any numeric operation slot, when receiving something other then ``PyNumber``,
refuses to implement it.
The number type does not define nb_coerce
Any numeric operation slot, when receiving something other then PyNumber,
refuses to implement it.
Inexact Operations
==================
The functions in the "math" module will be allowed to return
inexact results for exact values. However, they will never return
a non-real number. The functions in the "cmath" module are also
allowed to return an inexact result for an exact argument, and are
furthermore allowed to return a complex result for a real
argument.
The functions in the ``math`` module will be allowed to return
inexact results for exact values. However, they will never return
a non-real number. The functions in the ``cmath`` module are also
allowed to return an inexact result for an exact argument, and are
furthermore allowed to return a complex result for a real
argument.
Numerical Python Issues
=======================
People who use Numerical Python do so for high-performance vector
operations. Therefore, NumPy should keep its hardware based
numeric model.
People who use Numerical Python do so for high-performance vector
operations. Therefore, NumPy should keep its hardware based
numeric model.
Unresolved Issues
=================
Which number literals will be exact, and which inexact?
Which number literals will be exact, and which inexact?
How do we deal with IEEE 754 operations? (probably, isnan/isinf should
be methods)
How do we deal with IEEE 754 operations? (probably, isnan/isinf should
be methods)
On 64-bit machines, comparisons between ints and floats may be
broken when the comparison involves conversion to float. Ditto
for comparisons between longs and floats. This can be dealt with
by avoiding the conversion to float. (Due to Andrew Koenig.)
On 64-bit machines, comparisons between ints and floats may be
broken when the comparison involves conversion to float. Ditto
for comparisons between longs and floats. This can be dealt with
by avoiding the conversion to float. (Due to Andrew Koenig.)
Copyright
=========
This document has been placed in the public domain.
This document has been placed in the public domain.
Local Variables:
mode: indented-text
indent-tabs-mode: nil
End:
..
Local Variables:
mode: indented-text
indent-tabs-mode: nil
End:

View File

@ -5,27 +5,30 @@ Last-Modified: $Date$
Author: Tim Peters <tim.peters@gmail.com>
Status: Final
Type: Standards Track
Content-Type: text/x-rst
Created:
Python-Version: 2.1
Post-History: 16 February 2001
Note
====
This is essentially a retroactive PEP: the issue came up too late
in the 2.1 release process to solicit wide opinion before deciding
what to do, and can't be put off until 2.2 without also delaying
the Cygwin and MacOS X ports.
This is essentially a retroactive PEP: the issue came up too late
in the 2.1 release process to solicit wide opinion before deciding
what to do, and can't be put off until 2.2 without also delaying
the Cygwin and MacOS X ports.
Motivation
==========
File systems vary across platforms in whether or not they preserve
the case of filenames, and in whether or not the platform C
library file-opening functions do or don't insist on
case-sensitive matches:
File systems vary across platforms in whether or not they preserve
the case of filenames, and in whether or not the platform C
library file-opening functions do or don't insist on
case-sensitive matches::
case-preserving case-destroying
case-preserving case-destroying
+-------------------+------------------+
case-sensitive | most Unix flavors | brrrrrrrrrr |
+-------------------+------------------+
@ -35,119 +38,122 @@ Motivation
| | OpenVMS |
+-------------------+------------------+
In the upper left box, if you create "fiLe" it's stored as "fiLe",
and only open("fiLe") will open it (open("file") will not, nor
will the 14 other variations on that theme).
In the upper left box, if you create "fiLe" it's stored as "fiLe",
and only ``open("fiLe")`` will open it ``(open("file")`` will not, nor
will the 14 other variations on that theme).
In the lower right box, if you create "fiLe", there's no telling
what it's stored as -- but most likely as "FILE" -- and any of the
16 obvious variations on open("FilE") will open it.
In the lower right box, if you create "fiLe", there's no telling
what it's stored as -- but most likely as "FILE" -- and any of the
16 obvious variations on ``open("FilE")`` will open it.
The lower left box is a mix: creating "fiLe" stores "fiLe" in the
platform directory, but you don't have to match case when opening
it; any of the 16 obvious variations on open("FILe") work.
The lower left box is a mix: creating "fiLe" stores "fiLe" in the
platform directory, but you don't have to match case when opening
it; any of the 16 obvious variations on ``open("FILe")`` work.
NONE OF THAT IS CHANGING! Python will continue to follow platform
conventions w.r.t. whether case is preserved when creating a file,
and w.r.t. whether open() requires a case-sensitive match. In
practice, you should always code as if matches were
case-sensitive, else your program won't be portable.
NONE OF THAT IS CHANGING! Python will continue to follow platform
conventions w.r.t. whether case is preserved when creating a file,
and w.r.t. whether ``open()`` requires a case-sensitive match. In
practice, you should always code as if matches were
case-sensitive, else your program won't be portable.
What's proposed is to change the semantics of Python "import"
statements, and there *only* in the lower left box.
What's proposed is to change the semantics of Python "import"
statements, and there *only* in the lower left box.
Current Lower-Left Semantics
============================
Support for MacOSX HFS+, and for Cygwin, is new in 2.1, so nothing
is changing there. What's changing is Windows behavior. Here are
the current rules for import on Windows:
Support for MacOSX HFS+, and for Cygwin, is new in 2.1, so nothing
is changing there. What's changing is Windows behavior. Here are
the current rules for import on Windows:
1. Despite that the filesystem is case-insensitive, Python insists
on a case-sensitive match. But not in the way the upper left
box works: if you have two files, FiLe.py and file.py on
sys.path, and do
1. Despite that the filesystem is case-insensitive, Python insists
on a case-sensitive match. But not in the way the upper left
box works: if you have two files, ``FiLe.py`` and ``file.py`` on
``sys.path``, and do::
import file
import file
then if Python finds FiLe.py first, it raises a NameError. It
does *not* go on to find file.py; indeed, it's impossible to
import any but the first case-insensitive match on sys.path,
and then only if case matches exactly in the first
case-insensitive match.
then if Python finds ``FiLe.py`` first, it raises a ``NameError``. It
does *not* go on to find ``file.py``; indeed, it's impossible to
import any but the first case-insensitive match on ``sys.path``,
and then only if case matches exactly in the first
case-insensitive match.
2. An ugly exception: if the first case-insensitive match on
sys.path is for a file whose name is entirely in upper case
(FILE.PY or FILE.PYC or FILE.PYO), then the import silently
grabs that, no matter what mixture of case was used in the
import statement. This is apparently to cater to miserable old
filesystems that really fit in the lower right box. But this
exception is unique to Windows, for reasons that may or may not
exist.
2. An ugly exception: if the first case-insensitive match on
``sys.path`` is for a file whose name is entirely in upper case
(``FILE.PY`` or ``FILE.PYC`` or ``FILE.PYO``), then the import silently
grabs that, no matter what mixture of case was used in the
import statement. This is apparently to cater to miserable old
filesystems that really fit in the lower right box. But this
exception is unique to Windows, for reasons that may or may not
exist.
3. And another exception: if the environment variable PYTHONCASEOK
exists, Python silently grabs the first case-insensitive match
of any kind.
3. And another exception: if the environment variable ``PYTHONCASEOK``
exists, Python silently grabs the first case-insensitive match
of any kind.
So these Windows rules are pretty complicated, and neither match
the Unix rules nor provide semantics natural for the native
filesystem. That makes them hard to explain to Unix *or* Windows
users. Nevertheless, they've worked fine for years, and in
isolation there's no compelling reason to change them.
So these Windows rules are pretty complicated, and neither match
the Unix rules nor provide semantics natural for the native
filesystem. That makes them hard to explain to Unix *or* Windows
users. Nevertheless, they've worked fine for years, and in
isolation there's no compelling reason to change them.
However, that was before the MacOSX HFS+ and Cygwin ports arrived.
They also have case-preserving case-insensitive filesystems, but
the people doing the ports despised the Windows rules. Indeed, a
patch to make HFS+ act like Unix for imports got past a reviewer
and into the code base, which incidentally made Cygwin also act
like Unix (but this met the unbounded approval of the Cygwin
folks, so they sure didn't complain -- they had patches of their
own pending to do this, but the reviewer for those balked).
However, that was before the MacOSX HFS+ and Cygwin ports arrived.
They also have case-preserving case-insensitive filesystems, but
the people doing the ports despised the Windows rules. Indeed, a
patch to make HFS+ act like Unix for imports got past a reviewer
and into the code base, which incidentally made Cygwin also act
like Unix (but this met the unbounded approval of the Cygwin
folks, so they sure didn't complain -- they had patches of their
own pending to do this, but the reviewer for those balked).
At a higher level, we want to keep Python consistent, by following
the same rules on *all* platforms with case-preserving
case-insensitive filesystems.
At a higher level, we want to keep Python consistent, by following
the same rules on *all* platforms with case-preserving
case-insensitive filesystems.
Proposed Semantics
==================
The proposed new semantics for the lower left box:
The proposed new semantics for the lower left box:
A. If the PYTHONCASEOK environment variable exists, same as
before: silently accept the first case-insensitive match of any
kind; raise ImportError if none found.
A. If the ``PYTHONCASEOK`` environment variable exists, same as
before: silently accept the first case-insensitive match of any
kind; raise ImportError if none found.
B. Else search sys.path for the first case-sensitive match; raise
ImportError if none found.
B. Else search ``sys.path`` for the first case-sensitive match; raise
``ImportError`` if none found.
#B is the same rule as is used on Unix, so this will improve cross-
platform portability. That's good. #B is also the rule the Mac
and Cygwin folks want (and wanted enough to implement themselves,
multiple times, which is a powerful argument in PythonLand). It
can't cause any existing non-exceptional Windows import to fail,
because any existing non-exceptional Windows import finds a
case-sensitive match first in the path -- and it still will. An
exceptional Windows import currently blows up with a NameError or
ImportError, in which latter case it still will, or in which
former case will continue searching, and either succeed or blow up
with an ImportError.
#B is the same rule as is used on Unix, so this will improve cross-
platform portability. That's good. #B is also the rule the Mac
and Cygwin folks want (and wanted enough to implement themselves,
multiple times, which is a powerful argument in PythonLand). It
can't cause any existing non-exceptional Windows import to fail,
because any existing non-exceptional Windows import finds a
case-sensitive match first in the path -- and it still will. An
exceptional Windows import currently blows up with a ``NameError`` or
``ImportError``, in which latter case it still will, or in which
former case will continue searching, and either succeed or blow up
with an ``ImportError``.
#A is needed to cater to case-destroying filesystems mounted on Windows,
and *may* also be used by people so enamored of "natural" Windows
behavior that they're willing to set an environment variable to
get it. I don't intend to implement #A for Unix too, but that's
just because I'm not clear on how I *could* do so efficiently (I'm
not going to slow imports under Unix just for theoretical purity).
#A is needed to cater to case-destroying filesystems mounted on Windows,
and *may* also be used by people so enamored of "natural" Windows
behavior that they're willing to set an environment variable to
get it. I don't intend to implement #A for Unix too, but that's
just because I'm not clear on how I *could* do so efficiently (I'm
not going to slow imports under Unix just for theoretical purity).
The potential damage is here: #2 (matching on ALLCAPS.PY) is
proposed to be dropped. Case-destroying filesystems are a
vanishing breed, and support for them is ugly. We're already
supporting (and will continue to support) PYTHONCASEOK for their
benefit, but they don't deserve multiple hacks in 2001.
The potential damage is here: #2 (matching on ``ALLCAPS.PY``) is
proposed to be dropped. Case-destroying filesystems are a
vanishing breed, and support for them is ugly. We're already
supporting (and will continue to support) ``PYTHONCASEOK`` for their
benefit, but they don't deserve multiple hacks in 2001.
Local Variables:
mode: indented-text
indent-tabs-mode: nil
End:
..
Local Variables:
mode: indented-text
indent-tabs-mode: nil
End:

View File

@ -5,184 +5,216 @@ Last-Modified: $Date$
Author: jriehl@spaceship.com (Jonathan Riehl)
Status: Deferred
Type: Standards Track
Content-Type: text/x-rst
Created: 24-Aug-2001
Python-Version: 2.2
Post-History:
Abstract
========
Much like the parser module exposes the Python parser, this PEP
proposes that the parser generator used to create the Python
parser, pgen, be exposed as a module in Python.
Much like the parser module exposes the Python parser, this PEP
proposes that the parser generator used to create the Python
parser, ``pgen``, be exposed as a module in Python.
Rationale
=========
Through the course of Pythonic history, there have been numerous
discussions about the creation of a Python compiler [1]. These
have resulted in several implementations of Python parsers, most
notably the parser module currently provided in the Python
standard library[2] and Jeremy Hylton's compiler module[3].
However, while multiple language changes have been proposed
[4][5], experimentation with the Python syntax has lacked the
benefit of a Python binding to the actual parser generator used to
build Python.
Through the course of Pythonic history, there have been numerous
discussions about the creation of a Python compiler [1]_. These
have resulted in several implementations of Python parsers, most
notably the parser module currently provided in the Python
standard library [2]_ and Jeremy Hylton's compiler module [3]_.
However, while multiple language changes have been proposed
[4]_ [5]_, experimentation with the Python syntax has lacked the
benefit of a Python binding to the actual parser generator used to
build Python.
By providing a Python wrapper analogous to Fred Drake Jr.'s parser
wrapper, but targeted at the pgen library, the following
assertions are made:
By providing a Python wrapper analogous to Fred Drake Jr.'s parser
wrapper, but targeted at the ``pgen`` library, the following
assertions are made:
1. Reference implementations of syntax changes will be easier to
develop. Currently, a reference implementation of a syntax
change would require the developer to use the pgen tool from
the command line. The resulting parser data structure would
then either have to be reworked to interface with a custom
CPython implementation, or wrapped as a C extension module.
1. Reference implementations of syntax changes will be easier to
develop. Currently, a reference implementation of a syntax
change would require the developer to use the ``pgen`` tool from
the command line. The resulting parser data structure would
then either have to be reworked to interface with a custom
CPython implementation, or wrapped as a C extension module.
2. Reference implementations of syntax changes will be easier to
distribute. Since the parser generator will be available in
Python, it should follow that the resulting parser will
accessible from Python. Therefore, reference implementations
should be available as pure Python code, versus using custom
versions of the existing CPython distribution, or as compilable
extension modules.
2. Reference implementations of syntax changes will be easier to
distribute. Since the parser generator will be available in
Python, it should follow that the resulting parser will
accessible from Python. Therefore, reference implementations
should be available as pure Python code, versus using custom
versions of the existing CPython distribution, or as compilable
extension modules.
3. Reference implementations of syntax changes will be easier to
discuss with a larger audience. This somewhat falls out of the
second assertion, since the community of Python users is most
likely larger than the community of CPython developers.
3. Reference implementations of syntax changes will be easier to
discuss with a larger audience. This somewhat falls out of the
second assertion, since the community of Python users is most
likely larger than the community of CPython developers.
4. Development of small languages in Python will be further
enhanced, since the additional module will be a fully
functional LL(1) parser generator.
4. Development of small languages in Python will be further
enhanced, since the additional module will be a fully
functional LL(1) parser generator.
Specification
=============
The proposed module will be called pgen. The pgen module will
contain the following functions:
The proposed module will be called ``pgen``. The ``pgen`` module will
contain the following functions:
parseGrammarFile (fileName) -> AST
The parseGrammarFile() function will read the file pointed to
by fileName and create an AST object. The AST nodes will
contain the nonterminal, numeric values of the parser
generator meta-grammar. The output AST will be an instance of
the AST extension class as provided by the parser module.
Syntax errors in the input file will cause the SyntaxError
exception to be raised.
parseGrammarString (text) -> AST
The parseGrammarString() function will follow the semantics of
the parseGrammarFile(), but accept the grammar text as a
string for input, as opposed to the file name.
``parseGrammarFile (fileName) -> AST``
--------------------------------------
buildParser (grammarAst) -> DFA
The buildParser() function will accept an AST object for input
and return a DFA (deterministic finite automaton) data
structure. The DFA data structure will be a C extension
class, much like the AST structure is provided in the parser
module. If the input AST does not conform to the nonterminal
codes defined for the pgen meta-grammar, buildParser() will
throw a ValueError exception.
The ``parseGrammarFile()`` function will read the file pointed to
by fileName and create an AST object. The AST nodes will
contain the nonterminal, numeric values of the parser
generator meta-grammar. The output AST will be an instance of
the AST extension class as provided by the parser module.
Syntax errors in the input file will cause the SyntaxError
exception to be raised.
parseFile (fileName, dfa, start) -> AST
The parseFile() function will essentially be a wrapper for the
PyParser_ParseFile() C API function. The wrapper code will
accept the DFA C extension class, and the file name. An AST
instance that conforms to the lexical values in the token
module and the nonterminal values contained in the DFA will be
output.
parseString (text, dfa, start) -> AST
The parseString() function will operate in a similar fashion
to the parseFile() function, but accept the parse text as an
argument. Much like parseFile() will wrap the
PyParser_ParseFile() C API function, parseString() will wrap
the PyParser_ParseString() function.
``parseGrammarString (text) -> AST``
------------------------------------
symbolToStringMap (dfa) -> dict
The symbolToStringMap() function will accept a DFA instance
and return a dictionary object that maps from the DFA's
numeric values for its nonterminals to the string names of the
nonterminals as found in the original grammar specification
for the DFA.
The ``parseGrammarString()`` function will follow the semantics of
the ``parseGrammarFile()``, but accept the grammar text as a
string for input, as opposed to the file name.
stringToSymbolMap (dfa) -> dict
The stringToSymbolMap() function output a dictionary mapping
the nonterminal names of the input DFA to their corresponding
numeric values.
Extra credit will be awarded if the map generation functions and
parsing functions are also methods of the DFA extension class.
``buildParser (grammarAst) -> DFA``
-----------------------------------
The ``buildParser()`` function will accept an AST object for input
and return a DFA (deterministic finite automaton) data
structure. The DFA data structure will be a C extension
class, much like the AST structure is provided in the parser
module. If the input AST does not conform to the nonterminal
codes defined for the ``pgen`` meta-grammar, ``buildParser()`` will
throw a ``ValueError`` exception.
``parseFile (fileName, dfa, start) -> AST``
-------------------------------------------
The ``parseFile()`` function will essentially be a wrapper for the
``PyParser_ParseFile()`` C API function. The wrapper code will
accept the DFA C extension class, and the file name. An AST
instance that conforms to the lexical values in the token
module and the nonterminal values contained in the DFA will be
output.
``parseString (text, dfa, start) -> AST``
-----------------------------------------
The ``parseString()`` function will operate in a similar fashion
to the ``parseFile()`` function, but accept the parse text as an
argument. Much like ``parseFile()`` will wrap the
``PyParser_ParseFile()`` C API function, ``parseString()`` will wrap
the ``PyParser_ParseString()`` function.
``symbolToStringMap (dfa) -> dict``
-----------------------------------
The ``symbolToStringMap()`` function will accept a DFA instance
and return a dictionary object that maps from the DFA's
numeric values for its nonterminals to the string names of the
nonterminals as found in the original grammar specification
for the DFA.
``stringToSymbolMap (dfa) -> dict``
-----------------------------------
The ``stringToSymbolMap()`` function output a dictionary mapping
the nonterminal names of the input DFA to their corresponding
numeric values.
Extra credit will be awarded if the map generation functions and
parsing functions are also methods of the DFA extension class.
Implementation Plan
===================
A cunning plan has been devised to accomplish this enhancement:
A cunning plan has been devised to accomplish this enhancement:
1. Rename the pgen functions to conform to the CPython naming
standards. This action may involve adding some header files to
the Include subdirectory.
1. Rename the ``pgen`` functions to conform to the CPython naming
standards. This action may involve adding some header files to
the Include subdirectory.
2. Move the pgen C modules in the Makefile.pre.in from unique pgen
elements to the Python C library.
2. Move the ``pgen`` C modules in the Makefile.pre.in from unique ``pgen``
elements to the Python C library.
3. Make any needed changes to the parser module so the AST
extension class understands that there are AST types it may not
understand. Cursory examination of the AST extension class
shows that it keeps track of whether the tree is a suite or an
expression.
3. Make any needed changes to the parser module so the AST
extension class understands that there are AST types it may not
understand. Cursory examination of the AST extension class
shows that it keeps track of whether the tree is a suite or an
expression.
3. Code an additional C module in the Modules directory. The C
extension module will implement the DFA extension class and the
functions outlined in the previous section.
3. Code an additional C module in the Modules directory. The C
extension module will implement the DFA extension class and the
functions outlined in the previous section.
4. Add the new module to the build process. Black magic, indeed.
4. Add the new module to the build process. Black magic, indeed.
Limitations
===========
Under this proposal, would be designers of Python 3000 will still
be constrained to Python's lexical conventions. The addition,
subtraction or modification of the Python lexer is outside the
scope of this PEP.
Under this proposal, would be designers of Python 3000 will still
be constrained to Python's lexical conventions. The addition,
subtraction or modification of the Python lexer is outside the
scope of this PEP.
Reference Implementation
========================
No reference implementation is currently provided. A patch
was provided at some point in
http://sourceforge.net/tracker/index.php?func=detail&aid=599331&group_id=5470&atid=305470
but that patch is no longer maintained.
No reference implementation is currently provided. A patch
was provided at some point in
http://sourceforge.net/tracker/index.php?func=detail&aid=599331&group_id=5470&atid=305470
but that patch is no longer maintained.
References
==========
[1] The (defunct) Python Compiler-SIG
http://www.python.org/sigs/compiler-sig/
.. [1] The (defunct) Python Compiler-SIG
http://www.python.org/sigs/compiler-sig/
[2] Parser Module Documentation
http://docs.python.org/library/parser.html
.. [2] Parser Module Documentation
http://docs.python.org/library/parser.html
[3] Hylton, Jeremy.
http://docs.python.org/library/compiler.html
.. [3] Hylton, Jeremy.
http://docs.python.org/library/compiler.html
[4] Pelletier, Michel. "Python Interface Syntax", PEP-245.
http://www.python.org/dev/peps/pep-0245/
.. [4] Pelletier, Michel. "Python Interface Syntax", PEP-245.
http://www.python.org/dev/peps/pep-0245/
[5] The Python Types-SIG
http://www.python.org/sigs/types-sig/
.. [5] The Python Types-SIG
http://www.python.org/sigs/types-sig/
Copyright
=========
This document has been placed in the public domain.
This document has been placed in the public domain.
Local Variables:
mode: indented-text
indent-tabs-mode: nil
fill-column: 70
End:
..
Local Variables:
mode: indented-text
indent-tabs-mode: nil
fill-column: 70
End:

View File

@ -5,159 +5,168 @@ Last-Modified: $Date$
Author: python@rcn.com (Raymond Hettinger)
Status: Withdrawn
Type: Standards Track
Content-Type: text/x-rst
Created: 21-Mar-2002
Python-Version: 2.5
Post-History:
Abstract
========
This PEP proposes to enhance generators by providing mechanisms for
raising exceptions and sharing data with running generators.
This PEP proposes to enhance generators by providing mechanisms for
raising exceptions and sharing data with running generators.
Status
======
This PEP is withdrawn. The exception raising mechanism was extended
and subsumed into PEP 343. The attribute passing capability
never built a following, did not have a clear implementation,
and did not have a clean way for the running generator to access
its own namespace.
This PEP is withdrawn. The exception raising mechanism was extended
and subsumed into PEP 343. The attribute passing capability
never built a following, did not have a clear implementation,
and did not have a clean way for the running generator to access
its own namespace.
Rationale
=========
Currently, only class based iterators can provide attributes and
exception handling. However, class based iterators are harder to
write, less compact, less readable, and slower. A better solution
is to enable these capabilities for generators.
Currently, only class based iterators can provide attributes and
exception handling. However, class based iterators are harder to
write, less compact, less readable, and slower. A better solution
is to enable these capabilities for generators.
Enabling attribute assignments allows data to be passed to and from
running generators. The approach of sharing data using attributes
pervades Python. Other approaches exist but are somewhat hackish
in comparison.
Enabling attribute assignments allows data to be passed to and from
running generators. The approach of sharing data using attributes
pervades Python. Other approaches exist but are somewhat hackish
in comparison.
Another evolutionary step is to add a generator method to allow
exceptions to be passed to a generator. Currently, there is no
clean method for triggering exceptions from outside the generator.
Also, generator exception passing helps mitigate the try/finally
prohibition for generators. The need is especially acute for
generators needing to flush buffers or close resources upon termination.
The two proposals are backwards compatible and require no new
keywords. They are being recommended for Python version 2.5.
Another evolutionary step is to add a generator method to allow
exceptions to be passed to a generator. Currently, there is no
clean method for triggering exceptions from outside the generator.
Also, generator exception passing helps mitigate the try/finally
prohibition for generators. The need is especially acute for
generators needing to flush buffers or close resources upon termination.
The two proposals are backwards compatible and require no new
keywords. They are being recommended for Python version 2.5.
Specification for Generator Attributes
======================================
Essentially, the proposal is to emulate attribute writing for classes.
The only wrinkle is that generators lack a way to refer to instances of
themselves. So, the proposal is to provide a function for discovering
the reference. For example:
Essentially, the proposal is to emulate attribute writing for classes.
The only wrinkle is that generators lack a way to refer to instances of
themselves. So, the proposal is to provide a function for discovering
the reference. For example::
def mygen(filename):
self = sys.get_generator()
myfile = open(filename)
for line in myfile:
if len(line) < 10:
continue
self.pos = myfile.tell()
yield line.upper()
def mygen(filename):
self = sys.get_generator()
myfile = open(filename)
for line in myfile:
if len(line) < 10:
continue
self.pos = myfile.tell()
yield line.upper()
g = mygen('sample.txt')
line1 = g.next()
print 'Position', g.pos
g = mygen('sample.txt')
line1 = g.next()
print 'Position', g.pos
Uses for generator attributes include:
Uses for generator attributes include:
1. Providing generator clients with extra information (as shown
above).
2. Externally setting control flags governing generator operation
(possibly telling a generator when to step in or step over
data groups).
3. Writing lazy consumers with complex execution states
(an arithmetic encoder output stream for example).
4. Writing co-routines (as demonstrated in Dr. Mertz's articles [1]).
1. Providing generator clients with extra information (as shown
above).
2. Externally setting control flags governing generator operation
(possibly telling a generator when to step in or step over
data groups).
3. Writing lazy consumers with complex execution states
(an arithmetic encoder output stream for example).
4. Writing co-routines (as demonstrated in Dr. Mertz's articles [1]_).
The control flow of 'yield' and 'next' is unchanged by this
proposal. The only change is that data can passed to and from the
generator. Most of the underlying machinery is already in place,
only the access function needs to be added.
The control flow of 'yield' and 'next' is unchanged by this
proposal. The only change is that data can passed to and from the
generator. Most of the underlying machinery is already in place,
only the access function needs to be added.
Specification for Generator Exception Passing:
Specification for Generator Exception Passing
=============================================
Add a .throw(exception) method to the generator interface:
Add a .throw(exception) method to the generator interface::
def logger():
start = time.time()
log = []
try:
while True:
log.append(time.time() - start)
yield log[-1]
except WriteLog:
writelog(log)
def logger():
start = time.time()
log = []
try:
while True:
log.append(time.time() - start)
yield log[-1]
except WriteLog:
writelog(log)
g = logger()
for i in [10,20,40,80,160]:
testsuite(i)
g.next()
g.throw(WriteLog)
g = logger()
for i in [10,20,40,80,160]:
testsuite(i)
g.next()
g.throw(WriteLog)
There is no existing work-around for triggering an exception
inside a generator. It is the only case in Python where active
code cannot be excepted to or through.
There is no existing work-around for triggering an exception
inside a generator. It is the only case in Python where active
code cannot be excepted to or through.
Generator exception passing also helps address an intrinsic
limitation on generators, the prohibition against their using
try/finally to trigger clean-up code [2].
Generator exception passing also helps address an intrinsic
limitation on generators, the prohibition against their using
try/finally to trigger clean-up code [2]_.
Note A: The name of the throw method was selected for several
reasons. Raise is a keyword and so cannot be used as a method
name. Unlike raise which immediately raises an exception from the
current execution point, throw will first return to the generator
and then raise the exception. The word throw is suggestive of
putting the exception in another location. The word throw is
already associated with exceptions in other languages.
Note A: The name of the throw method was selected for several
reasons. Raise is a keyword and so cannot be used as a method
name. Unlike raise which immediately raises an exception from the
current execution point, throw will first return to the generator
and then raise the exception. The word throw is suggestive of
putting the exception in another location. The word throw is
already associated with exceptions in other languages.
Alternative method names were considered: resolve(), signal(),
genraise(), raiseinto(), and flush(). None of these fit as well
as throw().
Alternative method names were considered: ``resolve()``, ``signal()``,
``genraise()``, ``raiseinto()``, and ``flush()``. None of these fit as well
as ``throw()``.
Note B: To keep the throw() syntax simple only the instance
version of the raise syntax would be supported (no variants for
"raise string" or "raise class, instance").
Note B: To keep the ``throw()`` syntax simple only the instance
version of the raise syntax would be supported (no variants for
"raise string" or "raise class, instance").
Calling "g.throw(instance)" would correspond to writing
"raise instance" immediately after the most recent yield.
Calling ``g.throw(instance)`` would correspond to writing
``raise instance`` immediately after the most recent yield.
References
==========
[1] Dr. David Mertz's draft columns for Charming Python:
http://gnosis.cx/publish/programming/charming_python_b5.txt
http://gnosis.cx/publish/programming/charming_python_b7.txt
.. [1] Dr. David Mertz's draft columns for Charming Python
http://gnosis.cx/publish/programming/charming_python_b5.txt
http://gnosis.cx/publish/programming/charming_python_b7.txt
[2] PEP 255 Simple Generators:
http://www.python.org/dev/peps/pep-0255/
.. [2] PEP 255 Simple Generators
http://www.python.org/dev/peps/pep-0255/
[3] Proof-of-concept recipe:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/164044
.. [3] Proof-of-concept recipe
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/164044
Copyright
=========
This document has been placed in the public domain.
This document has been placed in the public domain.
Local Variables:
mode: indented-text
indent-tabs-mode: nil
fill-column: 70
End:
..
Local Variables:
mode: indented-text
indent-tabs-mode: nil
fill-column: 70
End:

View File

@ -5,181 +5,205 @@ Last-Modified: $Date$
Author: Roman Suzi <rnd@onego.ru>, Alex Martelli <aleaxit@gmail.com>
Status: Deferred
Type: Standards Track
Content-Type: text/plain
Content-Type: text/x-rst
Created: 11-Feb-2003
Python-Version: 2.4
Post-History:
Abstract
========
This PEP proposes to make argumentless lambda keyword optional in
some cases where it is not grammatically ambiguous.
This PEP proposes to make argumentless lambda keyword optional in
some cases where it is not grammatically ambiguous.
Deferral
========
The BDFL hates the unary colon syntax. This PEP needs to go back
to the drawing board and find a more Pythonic syntax (perhaps an
alternative unary operator). See python-dev discussion on
17 June 2005.
The BDFL hates the unary colon syntax. This PEP needs to go back
to the drawing board and find a more Pythonic syntax (perhaps an
alternative unary operator). See python-dev discussion on
17 June 2005 [1]_.
Also, it is probably a good idea to eliminate the alternative
propositions which have no chance at all. The examples section
is good and highlights the readability improvements. It would
carry more weight with additional examples and with real-world
referents (instead of the abstracted dummy calls to ``:A`` and ``:B``).
Also, it is probably a good idea to eliminate the alternative
propositions which have no chance at all. The examples section
is good and highlights the readability improvements. It would
carry more weight with additional examples and with real-world
referents (instead of the abstracted dummy calls to :A and :B).
Motivation
==========
Lambdas are useful for defining anonymous functions, e.g. for use
as callbacks or (pseudo)-lazy evaluation schemes. Often, lambdas
are not used when they would be appropriate, just because the
keyword "lambda" makes code look complex. Omitting lambda in some
special cases is possible, with small and backwards compatible
changes to the grammar, and provides a cheap cure against such
"lambdaphobia".
Lambdas are useful for defining anonymous functions, e.g. for use
as callbacks or (pseudo)-lazy evaluation schemes. Often, lambdas
are not used when they would be appropriate, just because the
keyword "lambda" makes code look complex. Omitting lambda in some
special cases is possible, with small and backwards compatible
changes to the grammar, and provides a cheap cure against such
"lambdaphobia".
Rationale
=========
Sometimes people do not use lambdas because they fear to introduce
a term with a theory behind it. This proposal makes introducing
argumentless lambdas easier, by omitting the "lambda" keyword.
itself. Implementation can be done simply changing grammar so it
lets the "lambda" keyword be implied in a few well-known cases.
In particular, adding surrounding brackets lets you specify
nullary lambda anywhere.
Sometimes people do not use lambdas because they fear to introduce
a term with a theory behind it. This proposal makes introducing
argumentless lambdas easier, by omitting the "lambda" keyword.
itself. Implementation can be done simply changing grammar so it
lets the "lambda" keyword be implied in a few well-known cases.
In particular, adding surrounding brackets lets you specify
nullary lambda anywhere.
Syntax
======
An argumentless "lambda" keyword can be omitted in the following
cases:
An argumentless "lambda" keyword can be omitted in the following
cases:
* immediately after "=" in named parameter assignment or default
value assignment;
* immediately after "=" in named parameter assignment or default
value assignment;
* immediately after "(" in any expression;
* immediately after "(" in any expression;
* immediately after a "," in a function argument list;
* immediately after a "," in a function argument list;
* immediately after a ":" in a dictionary literal; (not
implemented)
* immediately after a ":" in a dictionary literal; (not
implemented)
* in an assignment statement; (not implemented)
* in an assignment statement; (not implemented)
Examples of Use
===============
1) Inline "if":
1) Inline ``if``::
def ifelse(cond, true_part, false_part):
if cond:
return true_part()
else:
return false_part()
def ifelse(cond, true_part, false_part):
if cond:
return true_part()
else:
return false_part()
# old syntax:
print ifelse(a < b, lambda:A, lambda:B)
# old syntax:
print ifelse(a < b, lambda:A, lambda:B)
# new syntax:
print ifelse(a < b, :A, :B)
# new syntax:
print ifelse(a < b, :A, :B)
# parts A and B may require extensive processing, as in:
print ifelse(a < b, :ext_proc1(A), :ext_proc2(B))
# parts A and B may require extensive processing, as in:
print ifelse(a < b, :ext_proc1(A), :ext_proc2(B))
2) Locking:
2) Locking::
def with(alock, acallable):
alock.acquire()
try:
acallable()
finally:
alock.release()
def with(alock, acallable):
alock.acquire()
try:
acallable()
finally:
alock.release()
with(mylock, :x(y(), 23, z(), 'foo'))
with(mylock, :x(y(), 23, z(), 'foo'))
Implementation
==============
Implementation requires some tweaking of the Grammar/Grammar file
in the Python sources, and some adjustment of
Modules/parsermodule.c to make syntactic and pragmatic changes.
Implementation requires some tweaking of the Grammar/Grammar file
in the Python sources, and some adjustment of
Modules/parsermodule.c to make syntactic and pragmatic changes.
(Some grammar/parser guru is needed to make a full
implementation.)
(Some grammar/parser guru is needed to make a full
implementation.)
Here are the changes needed to Grammar to allow implicit lambda:
Here are the changes needed to Grammar to allow implicit lambda::
varargslist: (fpdef ['=' imptest] ',')* ('*' NAME [',' '**'
NAME] | '**' NAME) | fpdef ['=' imptest] (',' fpdef ['='
imptest])* [',']
varargslist: (fpdef ['=' imptest] ',')* ('*' NAME [',' '**'
NAME] | '**' NAME) | fpdef ['=' imptest] (',' fpdef ['='
imptest])* [',']
imptest: test | implambdef
imptest: test | implambdef
atom: '(' [imptestlist] ')' | '[' [listmaker] ']' |
'{' [dictmaker] '}' | '`' testlist1 '`' | NAME | NUMBER | STRING+
atom: '(' [imptestlist] ')' | '[' [listmaker] ']' |
'{' [dictmaker] '}' | '`' testlist1 '`' | NAME | NUMBER | STRING+
implambdef: ':' test
implambdef: ':' test
imptestlist: imptest (',' imptest)* [',']
imptestlist: imptest (',' imptest)* [',']
argument: [test '='] imptest
argument: [test '='] imptest
Three new non-terminals are needed: imptest for the place where
implicit lambda may occur, implambdef for the implicit lambda
definition itself, imptestlist for a place where imptest's may
occur.
Three new non-terminals are needed: ``imptest`` for the place where
implicit lambda may occur, ``implambdef`` for the implicit lambda
definition itself, ``imptestlist`` for a place where ``imptest``'s may
occur.
This implementation is not complete. First, because some files in
Parser module need to be updated. Second, some additional places
aren't implemented, see Syntax section above.
This implementation is not complete. First, because some files in
Parser module need to be updated. Second, some additional places
aren't implemented, see Syntax section above.
Discussion
==========
This feature is not a high-visibility one (the only novel part is
the absence of lambda). The feature is intended to make null-ary
lambdas more appealing syntactically, to provide lazy evaluation
of expressions in some simple cases. This proposal is not targeted
at more advanced cases (demanding arguments for the lambda).
This feature is not a high-visibility one (the only novel part is
the absence of lambda). The feature is intended to make null-ary
lambdas more appealing syntactically, to provide lazy evaluation
of expressions in some simple cases. This proposal is not targeted
at more advanced cases (demanding arguments for the lambda).
There is an alternative proposition for implicit lambda: implicit
lambda with unused arguments. In this case the function defined by
such lambda can accept any parameters, i.e. be equivalent to:
lambda *args: expr. This form would be more powerful. Grep in the
standard library revealed that such lambdas are indeed in use.
There is an alternative proposition for implicit lambda: implicit
lambda with unused arguments. In this case the function defined by
such lambda can accept any parameters, i.e. be equivalent to:
``lambda *args: expr``. This form would be more powerful. Grep in the
standard library revealed that such lambdas are indeed in use.
One more extension can provide a way to have a list of parameters
passed to a function defined by implicit lambda. However, such
parameters need some special name to be accessed and are unlikely
to be included in the language. Possible local names for such
parameters are: _, __args__, __. For example:
One more extension can provide a way to have a list of parameters
passed to a function defined by implicit lambda. However, such
parameters need some special name to be accessed and are unlikely
to be included in the language. Possible local names for such
parameters are: ``_``, ``__args__``, ``__``. For example::
reduce(:_[0] + _[1], [1,2,3], 0)
reduce(:__[0] + __[1], [1,2,3], 0)
reduce(:__args__[0] + __args__[1], [1,2,3], 0)
reduce(:_[0] + _[1], [1,2,3], 0)
reduce(:__[0] + __[1], [1,2,3], 0)
reduce(:__args__[0] + __args__[1], [1,2,3], 0)
These forms do not look very nice, and in the PEP author's opinion
do not justify the removal of the lambda keyword in such cases.
These forms do not look very nice, and in the PEP author's opinion
do not justify the removal of the lambda keyword in such cases.
Credits
=======
The idea of dropping lambda was first coined by Paul Rubin at 08
Feb 2003 16:39:30 -0800 in comp.lang.python while discussing the
thread "For review: PEP 308 - If-then-else expression" [2]_.
References
==========
.. [1] Guido van Rossum, Recommend accepting PEP 312 -- Simple Implicit Lambda
https://mail.python.org/pipermail/python-dev/2005-June/054304.html
.. [2] Guido van Rossum, For review: PEP 308 - If-then-else expression
https://mail.python.org/pipermail/python-dev/2003-February/033178.html
The idea of dropping lambda was first coined by Paul Rubin at 08
Feb 2003 16:39:30 -0800 in comp.lang.python while discussing the
thread "For review: PEP 308 - If-then-else expression".
Copyright
=========
This document has been placed in the public domain.
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
End:
..
Local Variables:
mode: indented-text
indent-tabs-mode: nil
sentence-end-double-space: t
fill-column: 70
End:

View File

@ -2,160 +2,176 @@ PEP: 315
Title: Enhanced While Loop
Version: $Revision$
Last-Modified: $Date$
Author: Raymond Hettinger <python@rcn.com>
W Isaac Carroll <icarroll@pobox.com>
Author: Raymond Hettinger <python@rcn.com>, W Isaac Carroll <icarroll@pobox.com>
Status: Rejected
Type: Standards Track
Content-Type: text/plain
Content-Type: text/x-rst
Created: 25-Apr-2003
Python-Version: 2.5
Post-History:
Abstract
========
This PEP proposes adding an optional "do" clause to the beginning
of the while loop to make loop code clearer and reduce errors
caused by code duplication.
This PEP proposes adding an optional "do" clause to the beginning
of the while loop to make loop code clearer and reduce errors
caused by code duplication.
Notice
======
Rejected; see
http://mail.python.org/pipermail/python-ideas/2013-June/021610.html
Rejected; see [1]_.
This PEP has been deferred since 2006; see
http://mail.python.org/pipermail/python-dev/2006-February/060718.html
This PEP has been deferred since 2006; see [2]_.
Subsequent efforts to revive the PEP in April 2009 did not
meet with success because no syntax emerged that could
compete with the following form:
Subsequent efforts to revive the PEP in April 2009 did not
meet with success because no syntax emerged that could
compete with the following form::
while True:
<setup code>
if not <condition>:
break
<loop body>
while True:
<setup code>
if not <condition>:
break
<loop body>
A syntax alternative to the one proposed in the PEP was found for
a basic do-while loop but it gained little support because the
condition was at the top:
A syntax alternative to the one proposed in the PEP was found for
a basic do-while loop but it gained little support because the
condition was at the top::
do ... while <cond>:
<loop body>
do ... while <cond>:
<loop body>
Users of the language are advised to use the while-True form with
an inner if-break when a do-while loop would have been appropriate.
Users of the language are advised to use the while-True form with
an inner if-break when a do-while loop would have been appropriate.
Motivation
==========
It is often necessary for some code to be executed before each
evaluation of the while loop condition. This code is often
duplicated outside the loop, as setup code that executes once
before entering the loop:
It is often necessary for some code to be executed before each
evaluation of the while loop condition. This code is often
duplicated outside the loop, as setup code that executes once
before entering the loop::
<setup code>
while <condition>:
<loop body>
<setup code>
while <condition>:
<loop body>
<setup code>
The problem is that duplicated code can be a source of errors if
one instance is changed but the other is not. Also, the purpose
of the second instance of the setup code is not clear because it
comes at the end of the loop.
The problem is that duplicated code can be a source of errors if
one instance is changed but the other is not. Also, the purpose
of the second instance of the setup code is not clear because it
comes at the end of the loop.
It is possible to prevent code duplication by moving the loop
condition into a helper function, or an if statement in the loop
body. However, separating the loop condition from the while
keyword makes the behavior of the loop less clear:
It is possible to prevent code duplication by moving the loop
condition into a helper function, or an if statement in the loop
body. However, separating the loop condition from the while
keyword makes the behavior of the loop less clear::
def helper(args):
<setup code>
return <condition>
def helper(args):
<setup code>
return <condition>
while helper(args):
<loop body>
while helper(args):
<loop body>
This last form has the additional drawback of requiring the loop's
else clause to be added to the body of the if statement, further
obscuring the loop's behavior:
This last form has the additional drawback of requiring the loop's
else clause to be added to the body of the if statement, further
obscuring the loop's behavior::
while True:
<setup code>
if not <condition>: break
<loop body>
while True:
<setup code>
if not <condition>: break
<loop body>
This PEP proposes to solve these problems by adding an optional
clause to the while loop, which allows the setup code to be
expressed in a natural way:
This PEP proposes to solve these problems by adding an optional
clause to the while loop, which allows the setup code to be
expressed in a natural way::
do:
<setup code>
while <condition>:
<loop body>
do:
<setup code>
while <condition>:
<loop body>
This keeps the loop condition with the while keyword where it
belongs, and does not require code to be duplicated.
This keeps the loop condition with the while keyword where it
belongs, and does not require code to be duplicated.
Syntax
======
The syntax of the while statement
The syntax of the while statement::
while_stmt : "while" expression ":" suite
["else" ":" suite]
while_stmt : "while" expression ":" suite
["else" ":" suite]
is extended as follows:
is extended as follows::
while_stmt : ["do" ":" suite]
"while" expression ":" suite
["else" ":" suite]
while_stmt : ["do" ":" suite]
"while" expression ":" suite
["else" ":" suite]
Semantics of break and continue
===============================
In the do-while loop the break statement will behave the same as
in the standard while loop: It will immediately terminate the loop
without evaluating the loop condition or executing the else
clause.
In the do-while loop the break statement will behave the same as
in the standard while loop: It will immediately terminate the loop
without evaluating the loop condition or executing the else
clause.
A continue statement in the do-while loop jumps to the while
condition check.
A continue statement in the do-while loop jumps to the while
condition check.
In general, when the while suite is empty (a pass statement),
the do-while loop and break and continue statements should match
the semantics of do-while in other languages.
In general, when the while suite is empty (a pass statement),
the do-while loop and break and continue statements should match
the semantics of do-while in other languages.
Likewise, when the do suite is empty, the do-while loop and
break and continue statements should match behavior found
in regular while loops.
Likewise, when the do suite is empty, the do-while loop and
break and continue statements should match behavior found
in regular while loops.
Future Statement
================
Because of the new keyword "do", the statement
Because of the new keyword "do", the statement::
from __future__ import do_while
from __future__ import do_while
will initially be required to use the do-while form.
will initially be required to use the do-while form.
Implementation
==============
The first implementation of this PEP can compile the do-while loop
as an infinite loop with a test that exits the loop.
The first implementation of this PEP can compile the do-while loop
as an infinite loop with a test that exits the loop.
References
==========
.. [1] Guido van Rossum, PEP 315: do-while
https://mail.python.org/pipermail/python-ideas/2013-June/021610.html
.. [2] Raymond Hettinger, release plan for 2.5 ?
https://mail.python.org/pipermail/python-dev/2006-February/060718.html
Copyright
=========
This document is placed in the public domain.
This document is placed in the public domain.
Local Variables:
mode: indented-text
indent-tabs-mode: nil
sentence-end-double-space: t
fill-column: 75
End:
..
Local Variables:
mode: indented-text
indent-tabs-mode: nil
sentence-end-double-space: t
fill-column: 75
End:

View File

@ -5,186 +5,193 @@ Last-Modified: $Date$
Author: Talin <viridia@gmail.com>
Status: Final
Type: Standards Track
Content-Type: text/plain
Content-Type: text/x-rst
Created: 22-Apr-2006
Python-Version: 3.0
Post-History: 28-Apr-2006, May-19-2006
Abstract
========
This PEP proposes a change to the way that function arguments are
assigned to named parameter slots. In particular, it enables the
declaration of "keyword-only" arguments: arguments that can only
be supplied by keyword and which will never be automatically
filled in by a positional argument.
This PEP proposes a change to the way that function arguments are
assigned to named parameter slots. In particular, it enables the
declaration of "keyword-only" arguments: arguments that can only
be supplied by keyword and which will never be automatically
filled in by a positional argument.
Rationale
=========
The current Python function-calling paradigm allows arguments to
be specified either by position or by keyword. An argument can be
filled in either explicitly by name, or implicitly by position.
The current Python function-calling paradigm allows arguments to
be specified either by position or by keyword. An argument can be
filled in either explicitly by name, or implicitly by position.
There are often cases where it is desirable for a function to take
a variable number of arguments. The Python language supports this
using the 'varargs' syntax ('*name'), which specifies that any
'left over' arguments be passed into the varargs parameter as a
tuple.
There are often cases where it is desirable for a function to take
a variable number of arguments. The Python language supports this
using the 'varargs' syntax (``*name``), which specifies that any
'left over' arguments be passed into the varargs parameter as a
tuple.
One limitation on this is that currently, all of the regular
argument slots must be filled before the vararg slot can be.
One limitation on this is that currently, all of the regular
argument slots must be filled before the vararg slot can be.
This is not always desirable. One can easily envision a function
which takes a variable number of arguments, but also takes one
or more 'options' in the form of keyword arguments. Currently,
the only way to do this is to define both a varargs argument,
and a 'keywords' argument (**kwargs), and then manually extract
the desired keywords from the dictionary.
This is not always desirable. One can easily envision a function
which takes a variable number of arguments, but also takes one
or more 'options' in the form of keyword arguments. Currently,
the only way to do this is to define both a varargs argument,
and a 'keywords' argument (``**kwargs``), and then manually extract
the desired keywords from the dictionary.
Specification
=============
Syntactically, the proposed changes are fairly simple. The first
change is to allow regular arguments to appear after a varargs
argument:
Syntactically, the proposed changes are fairly simple. The first
change is to allow regular arguments to appear after a varargs
argument::
def sortwords(*wordlist, case_sensitive=False):
...
def sortwords(*wordlist, case_sensitive=False):
...
This function accepts any number of positional arguments, and it
also accepts a keyword option called 'case_sensitive'. This
option will never be filled in by a positional argument, but
must be explicitly specified by name.
This function accepts any number of positional arguments, and it
also accepts a keyword option called 'case_sensitive'. This
option will never be filled in by a positional argument, but
must be explicitly specified by name.
Keyword-only arguments are not required to have a default value.
Since Python requires that all arguments be bound to a value,
and since the only way to bind a value to a keyword-only argument
is via keyword, such arguments are therefore 'required keyword'
arguments. Such arguments must be supplied by the caller, and
they must be supplied via keyword.
Keyword-only arguments are not required to have a default value.
Since Python requires that all arguments be bound to a value,
and since the only way to bind a value to a keyword-only argument
is via keyword, such arguments are therefore 'required keyword'
arguments. Such arguments must be supplied by the caller, and
they must be supplied via keyword.
The second syntactical change is to allow the argument name to
be omitted for a varargs argument. The meaning of this is to
allow for keyword-only arguments for functions that would not
otherwise take a varargs argument:
The second syntactical change is to allow the argument name to
be omitted for a varargs argument. The meaning of this is to
allow for keyword-only arguments for functions that would not
otherwise take a varargs argument::
def compare(a, b, *, key=None):
...
def compare(a, b, *, key=None):
...
The reasoning behind this change is as follows. Imagine for a
moment a function which takes several positional arguments, as
well as a keyword argument:
The reasoning behind this change is as follows. Imagine for a
moment a function which takes several positional arguments, as
well as a keyword argument::
def compare(a, b, key=None):
...
def compare(a, b, key=None):
...
Now, suppose you wanted to have 'key' be a keyword-only argument.
Under the above syntax, you could accomplish this by adding a
varargs argument immediately before the keyword argument:
Now, suppose you wanted to have 'key' be a keyword-only argument.
Under the above syntax, you could accomplish this by adding a
varargs argument immediately before the keyword argument::
def compare(a, b, *ignore, key=None):
...
def compare(a, b, *ignore, key=None):
...
Unfortunately, the 'ignore' argument will also suck up any
erroneous positional arguments that may have been supplied by the
caller. Given that we'd prefer any unwanted arguments to raise an
error, we could do this:
Unfortunately, the 'ignore' argument will also suck up any
erroneous positional arguments that may have been supplied by the
caller. Given that we'd prefer any unwanted arguments to raise an
error, we could do this::
def compare(a, b, *ignore, key=None):
if ignore: # If ignore is not empty
raise TypeError
def compare(a, b, *ignore, key=None):
if ignore: # If ignore is not empty
raise TypeError
As a convenient shortcut, we can simply omit the 'ignore' name,
meaning 'don't allow any positional arguments beyond this point'.
(Note: After much discussion of alternative syntax proposals, the
BDFL has pronounced in favor of this 'single star' syntax for
indicating the end of positional parameters.)
As a convenient shortcut, we can simply omit the 'ignore' name,
meaning 'don't allow any positional arguments beyond this point'.
(Note: After much discussion of alternative syntax proposals, the
BDFL has pronounced in favor of this 'single star' syntax for
indicating the end of positional parameters.)
Function Calling Behavior
=========================
The previous section describes the difference between the old
behavior and the new. However, it is also useful to have a
description of the new behavior that stands by itself, without
reference to the previous model. So this next section will
attempt to provide such a description.
The previous section describes the difference between the old
behavior and the new. However, it is also useful to have a
description of the new behavior that stands by itself, without
reference to the previous model. So this next section will
attempt to provide such a description.
When a function is called, the input arguments are assigned to
formal parameters as follows:
When a function is called, the input arguments are assigned to
formal parameters as follows:
- For each formal parameter, there is a slot which will be used
to contain the value of the argument assigned to that
parameter.
- For each formal parameter, there is a slot which will be used
to contain the value of the argument assigned to that
parameter.
- Slots which have had values assigned to them are marked as
'filled'. Slots which have no value assigned to them yet are
considered 'empty'.
- Slots which have had values assigned to them are marked as
'filled'. Slots which have no value assigned to them yet are
considered 'empty'.
- Initially, all slots are marked as empty.
- Initially, all slots are marked as empty.
- Positional arguments are assigned first, followed by keyword
arguments.
- Positional arguments are assigned first, followed by keyword
arguments.
- For each positional argument:
- For each positional argument:
o Attempt to bind the argument to the first unfilled
parameter slot. If the slot is not a vararg slot, then
mark the slot as 'filled'.
* Attempt to bind the argument to the first unfilled
parameter slot. If the slot is not a vararg slot, then
mark the slot as 'filled'.
o If the next unfilled slot is a vararg slot, and it does
not have a name, then it is an error.
* If the next unfilled slot is a vararg slot, and it does
not have a name, then it is an error.
o Otherwise, if the next unfilled slot is a vararg slot then
all remaining non-keyword arguments are placed into the
vararg slot.
* Otherwise, if the next unfilled slot is a vararg slot then
all remaining non-keyword arguments are placed into the
vararg slot.
- For each keyword argument:
- For each keyword argument:
o If there is a parameter with the same name as the keyword,
then the argument value is assigned to that parameter slot.
However, if the parameter slot is already filled, then that
is an error.
* If there is a parameter with the same name as the keyword,
then the argument value is assigned to that parameter slot.
However, if the parameter slot is already filled, then that
is an error.
o Otherwise, if there is a 'keyword dictionary' argument,
the argument is added to the dictionary using the keyword
name as the dictionary key, unless there is already an
entry with that key, in which case it is an error.
* Otherwise, if there is a 'keyword dictionary' argument,
the argument is added to the dictionary using the keyword
name as the dictionary key, unless there is already an
entry with that key, in which case it is an error.
o Otherwise, if there is no keyword dictionary, and no
matching named parameter, then it is an error.
* Otherwise, if there is no keyword dictionary, and no
matching named parameter, then it is an error.
- Finally:
- Finally:
o If the vararg slot is not yet filled, assign an empty tuple
as its value.
* If the vararg slot is not yet filled, assign an empty tuple
as its value.
o For each remaining empty slot: if there is a default value
for that slot, then fill the slot with the default value.
If there is no default value, then it is an error.
* For each remaining empty slot: if there is a default value
for that slot, then fill the slot with the default value.
If there is no default value, then it is an error.
In accordance with the current Python implementation, any errors
encountered will be signaled by raising TypeError. (If you want
something different, that's a subject for a different PEP.)
In accordance with the current Python implementation, any errors
encountered will be signaled by raising ``TypeError``. (If you want
something different, that's a subject for a different PEP.)
Backwards Compatibility
=======================
The function calling behavior specified in this PEP is a superset
of the existing behavior - that is, it is expected that any
existing programs will continue to work.
The function calling behavior specified in this PEP is a superset
of the existing behavior - that is, it is expected that any
existing programs will continue to work.
Copyright
=========
This document has been placed in the public domain.
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:
..
Local Variables:
mode: indented-text
indent-tabs-mode: nil
sentence-end-double-space: t
fill-column: 70
coding: utf-8
End: