Editorial review, including:

- Rearrange and standardize headers
    - Removed ^L's
    - Spellchecked
    - Indentation and formatting
    - Added reference to PEP 202

Thomas, if the open issues have been decided, they can be `closed' in
this PEP, and then it should probably be marked as Accepted.
This commit is contained in:
Barry Warsaw 2000-08-23 05:41:57 +00:00
parent 990dbb1692
commit e7669897fc
1 changed files with 120 additions and 115 deletions

View File

@ -1,38 +1,39 @@
PEP: 204
Title: Range Literals
Version: $Revision$
Owner: thomas@xs4all.net (Thomas Wouters)
Python-Version: 2.0
Author: thomas@xs4all.net (Thomas Wouters)
Status: Draft
Type: Standards Track
Python-Version: 2.0
Created: 14-Jul-2000
Post-History:
Introduction
This PEP describes the `range literal' proposal for Python 2.0.
This PEP describes the `range literal' proposal for Python 2.0.
This PEP tracks the status and ownership of this feature, slated
for introduction in Python 2.0. It contains a description of the
feature and outlines changes necessary to support the feature.
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.
List ranges
Ranges are sequences of numbers of a fixed stepping, often used in
for-loops. The Python for-loop is designed to iterate over a
sequence directly:
>>> l = ['a', 'b', 'c', 'd']
>>> for item in l:
... print item
a
b
c
d
>>> l = ['a', 'b', 'c', 'd']
>>> for item in l:
... print item
a
b
c
d
However, this solution is not always prudent. Firstly, problems
arise when altering the sequence in the body of the for-loop,
@ -56,25 +57,25 @@ List ranges
Using the `range' function, the above example would look like
this:
>>> for i in range(len(l)):
... print l[i]
a
b
c
d
>>> for i in range(len(l)):
... print l[i]
a
b
c
d
Or, to start at the second element of `l' and processing only
every second element from then on:
>>> for i in range(1, len(l), 2):
... print l[i]
b
d
>>> for i in range(1, len(l), 2):
... print l[i]
b
d
There are several disadvantages with this approach:
- Clarity of purpose: Adding another functioncall, possibly with
extra arithmatic to determine the desired length and step of the
- Clarity of purpose: Adding another function call, possibly with
extra arithmetic to determine the desired length and step of the
list, does not improve readability of the code. Also, it is
possible to `shadow' the builtin `range' function by supplying a
local or global variable with the same name, effectively
@ -82,7 +83,7 @@ List ranges
- Efficiency: because the `range' function can be overridden, the
Python compiler cannot make assumptions about the for-loop, and
has to maintain a seperate loop counter.
has to maintain a separate loop counter.
- Consistency: There already is a syntax that is used to denote
ranges, as shown below. This syntax uses the exact same
@ -91,7 +92,6 @@ List ranges
literals'.
Slice Indices
In Python, a sequence can be indexed in one of two ways:
@ -100,10 +100,10 @@ Slice Indices
type as the original sequence, containing zero or more items from
the original sequence. This is done using a `range notation':
>>> l[2:4]
['c', 'd']
>>> l[2:4]
['c', 'd']
This range notation consists of zero, one or two indices seperated
This range notation consists of zero, one or two indices separated
by a colon. The first index is the `start' index, the second the
`end'. When either is left out, they default to respectively the
start and the end of the sequence.
@ -112,16 +112,16 @@ Slice Indices
`step' as well. Though this notation is not currently supported
by most builtin types, if it were, it would work as follows:
>>> l[1:4:2]
['b', 'd']
>>> l[1:4:2]
['b', 'd']
The third `argument' to the slice syntax is exactly the same as
the `step' argument to range(). The underlying mechanisms of
standard and these extended slices are sufficiently different and
inconsistent that many classes and extensions outside of
the `step' argument to range(). The underlying mechanisms of the
standard, and these extended slices, are sufficiently different
and inconsistent that many classes and extensions outside of
mathematical packages do not implement support for the extended
variant, and this should definately be resolved, but this is
beyond the scope of this PEP.
variant. While this should be resolved, it is beyond the scope of
this PEP.
Extended slices do show, however, that there is already a
perfectly valid and applicable syntax to denote ranges in a way
@ -132,7 +132,7 @@ Slice Indices
be both intuitive and easy to learn.
- It is consistent with the other use of ranges in Python
(slices.)
(e.g. slices).
- Because it is built-in syntax, instead of a builtin function, it
cannot be overridden. This means both that a viewer can be
@ -140,19 +140,18 @@ Slice Indices
have to worry about range() being `shadowed'.
The Proposed Solution
The proposed implementation of range-literals combines the syntax
for list literals with the syntax for (extended) slices, to form
range literals:
>>> [1:10]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [:5]
[0, 1, 2, 3, 4]
>>> [5:1:-1]
[5, 4, 3, 2]
>>> [1:10]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [:5]
[0, 1, 2, 3, 4]
>>> [5:1:-1]
[5, 4, 3, 2]
There is one minor difference between range literals and the slice
syntax: though it is possible to omit all of `start', `end' and
@ -161,7 +160,6 @@ The Proposed Solution
but this has no meaning in range literals.
Reference Implementation
The proposed implementation can be found on SourceForge[1]. It
@ -177,93 +175,100 @@ Reference Implementation
currently hovering between private functions and full-fledged API
calls.
PyObject * PyList_FromRange(long start, long end, long step)
builds a list from start, end and step, returning NULL if an error
occurs.
long PyList_GetLenOfRange(long start, long end, long step) is a
helper function to determine the length of a range. It was
previously a static function in bltinmodule.c, but is now
necessary in both listobject.c and bltinmodule.c (for xrange). It
is made non-static solely to avoid code duplication.
PyList_FromRange() builds a list from start, end and step,
returning NULL if an error occurs. Its prototype is:
PyObject * PyList_FromRange(long start, long end, long step)
PyList_GetLenOfRange() is a helper function used to determine the
length of a range. Previously, it was a static function in
bltinmodule.c, but is now necessary in both listobject.c and
bltinmodule.c (for xrange). It is made non-static solely to avoid
code duplication. Its prototype is:
long PyList_GetLenOfRange(long start, long end, long step)
Open issues
One possible solution to the discrepancy of requiring the `end'
argument in range literals is to allow the range syntax to create
a `generator', rather than a list, such as the `xrange' builtin
function does. However, a generator would not be a list, and it
would be impossible, for instance, to assign to items in the
generator, or append to it.
The range syntax could conceivably be extended to include tuples,
immutable lists, which could then be safely implemented as
generators. Especially for large number arrays, this may be a
desirable solution: generators require very little in the way of
storage and initialization, and there is only a small performance
impact in calculating and creating the appropriate number on
request. (TBD: is there any at all ? Cursory testing suggests
equal performance even in the case of ranges of length 1.)
However, even if idea was adopted, would it be wise to `special
case' the second argument, making it optional in one instance of
the syntax, and non-optional in other cases ?
- One possible solution to the discrepancy of requiring the `end'
argument in range literals is to allow the range syntax to
create a `generator', rather than a list, such as the `xrange'
builtin function does. However, a generator would not be a
list, and it would be impossible, for instance, to assign to
items in the generator, or append to it.
The range syntax could conceivably be extended to include tuples
(i.e. immutable lists), which could then be safely implemented
as generators. This may be a desirable solution, especially for
large number arrays: generators require very little in the way
of storage and initialization, and there is only a small
performance impact in calculating and creating the appropriate
number on request. (TBD: is there any at all? Cursory testing
suggests equal performance even in the case of ranges of length
1)
Should it be possible to mix range syntax with normal list
literals, creating a single list, like so:
However, even if idea was adopted, would it be wise to `special
case' the second argument, making it optional in one instance of
the syntax, and non-optional in other cases ?
- Should it be possible to mix range syntax with normal list
literals, creating a single list? E.g.:
>>> [5, 6, 1:6, 7, 9]
>>> [5, 6, 1:6, 7, 9]
to create
[5, 6, 1, 2, 3, 4, 5, 7, 9]
[5, 6, 1, 2, 3, 4, 5, 7, 9]
How should range literals interact with another proposed new
feature, `list comprehensions', PEP-202 ? In specific, should it
be possible to create lists in list comprehensions, like so:
- How should range literals interact with another proposed new
feature, `list comprehensions'[2]? Specifically, should it be
possible to create lists in list comprehensions? E.g.:
>>> [x:y for x in (1,2) y in (3, 4)]
>>> [x:y for x in (1, 2) y in (3, 4)]
Should this example return a single list with multiple ranges:
[1, 2, 1, 2, 3, 2, 2, 3]
Should this example return a single list with multiple ranges:
Or a list of lists, like so:
[[1, 2], [1, 2, 3], [2], [2, 3]]
[1, 2, 1, 2, 3, 2, 2, 3]
However, as the syntax and semantics of list comprehensions are
still subject of hot debate, these issues are probably best
addressed by the `list comprehensions' PEP.
Or a list of lists, like so:
[[1, 2], [1, 2, 3], [2], [2, 3]]
However, as the syntax and semantics of list comprehensions are
still subject of hot debate, these issues are probably best
addressed by the `list comprehensions' PEP.
- Range literals accept objects other than integers: it performs
PyInt_AsLong() on the objects passed in, so as long as the
objects can be coerced into integers, they will be accepted.
The resulting list, however, is always composed of standard
integers.
Should range literals create a list of the passed-in type? It
might be desirable in the cases of other builtin types, such as
longs and strings:
>>> [ 1L : 2L<<64 : 2<<32L ]
>>> ["a":"z":"b"]
>>> ["a":"z":2]
However, this might be too much `magic' to be obvious. It might
also present problems with user-defined classes: even if the
base class can be found and a new instance created, the instance
may require additional arguments to __init__, causing the
creation to fail.
- The PyList_FromRange() and PyList_GetLenOfRange() functions need
to be classified: are they part of the API, or should they be
made private functions?
Range literals accept objects other than integers: it performs
PyInt_AsLong() on the objects passed in, so as long as the objects
can be coerced into integers, they will be accepted. The
resulting list, however, is always composed of standard integers.
Should range literals create a list of the passed-in type ? It
might be desirable in the cases of other builtin types, such as
longs and strings:
>>> [ 1L : 2L<<64 : 2<<32L ]
>>> ["a":"z":"b"]
>>> ["a":"z":2]
However, this might be too much `magic' to be obvious. It might
also present problems with user-defined classes: even if the base
class can be found and a new instance created, the instance may
require additional arguments to __init__, causing the creation to
fail.
The PyList_FromRange() and PyList_GetLenOfRange() functions need
to be classified: are they part of the API, or should they be made
private functions ?
References:
[1] http://sourceforge.net/patch/?func=detailpatch&patch_id=100902&group_id=5470
[2] PEP 202, List Comprehensions, pep-0202.txt
Local Variables: