summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaymond Hettinger <python@rcn.com>2003-11-06 14:06:48 (GMT)
committerRaymond Hettinger <python@rcn.com>2003-11-06 14:06:48 (GMT)
commit85c20a41dfcec04d161ad7da7260e7b94c62d228 (patch)
tree0d9e5b294ab4890b72ddc61d193036ac1d4b5ca4
parentf607fc5395883ff924c76739e9b0921953568e54 (diff)
downloadcpython-85c20a41dfcec04d161ad7da7260e7b94c62d228.zip
cpython-85c20a41dfcec04d161ad7da7260e7b94c62d228.tar.gz
cpython-85c20a41dfcec04d161ad7da7260e7b94c62d228.tar.bz2
Implement and apply PEP 322, reverse iteration
-rw-r--r--Doc/lib/libfuncs.tex8
-rw-r--r--Include/enumobject.h1
-rw-r--r--Lib/heapq.py2
-rw-r--r--Lib/mhlib.py3
-rwxr-xr-xLib/platform.py2
-rw-r--r--Lib/random.py2
-rw-r--r--Lib/rfc822.py3
-rw-r--r--Lib/test/test_enumerate.py20
-rw-r--r--Misc/NEWS3
-rw-r--r--Objects/enumobject.c125
-rw-r--r--Objects/rangeobject.c37
-rw-r--r--Python/bltinmodule.c1
12 files changed, 198 insertions, 9 deletions
diff --git a/Doc/lib/libfuncs.tex b/Doc/lib/libfuncs.tex
index a9f3a65..7d64f93 100644
--- a/Doc/lib/libfuncs.tex
+++ b/Doc/lib/libfuncs.tex
@@ -880,6 +880,14 @@ class C(object):
when passed to \function{eval()}.
\end{funcdesc}
+\begin{funcdesc}{reversed}{seq}
+ Return a reverse iterator. \var{seq} must be an object which
+ supports the sequence protocol (the __len__() method and the
+ \method{__getitem__()} method with integer arguments starting at
+ \code{0}).
+ \versionadded{2.4}
+\end{funcdesc}
+
\begin{funcdesc}{round}{x\optional{, n}}
Return the floating point value \var{x} rounded to \var{n} digits
after the decimal point. If \var{n} is omitted, it defaults to zero.
diff --git a/Include/enumobject.h b/Include/enumobject.h
index 053fb72..c14dbfc 100644
--- a/Include/enumobject.h
+++ b/Include/enumobject.h
@@ -8,6 +8,7 @@ extern "C" {
#endif
PyAPI_DATA(PyTypeObject) PyEnum_Type;
+PyAPI_DATA(PyTypeObject) PyReversed_Type;
#ifdef __cplusplus
}
diff --git a/Lib/heapq.py b/Lib/heapq.py
index 2c30b12..2223a97 100644
--- a/Lib/heapq.py
+++ b/Lib/heapq.py
@@ -165,7 +165,7 @@ def heapify(x):
# or i < (n-1)/2. If n is even = 2*j, this is (2*j-1)/2 = j-1/2 so
# j-1 is the largest, which is n//2 - 1. If n is odd = 2*j+1, this is
# (2*j+1-1)/2 = j so j-1 is the largest, and that's again n//2-1.
- for i in xrange(n//2 - 1, -1, -1):
+ for i in reversed(xrange(n//2)):
_siftup(x, i)
# 'heap' is a heap at all indices >= startpos, except possibly for pos. pos
diff --git a/Lib/mhlib.py b/Lib/mhlib.py
index 5520f82..899939a 100644
--- a/Lib/mhlib.py
+++ b/Lib/mhlib.py
@@ -975,8 +975,7 @@ def test():
print seqs
f.putsequences(seqs)
do('f.getsequences()')
- testfolders.reverse()
- for t in testfolders: do('mh.deletefolder(%s)' % `t`)
+ for t in reversed(testfolders): do('mh.deletefolder(%s)' % `t`)
do('mh.getcontext()')
context = mh.getcontext()
f = mh.openfolder(context)
diff --git a/Lib/platform.py b/Lib/platform.py
index 31c47b4..389e50c 100755
--- a/Lib/platform.py
+++ b/Lib/platform.py
@@ -201,7 +201,7 @@ def _dist_try_harder(distname,version,id):
if os.path.isdir('/usr/lib/setup'):
# Check for slackware verson tag file (thanks to Greg Andruk)
verfiles = os.listdir('/usr/lib/setup')
- for n in range(len(verfiles)-1, -1, -1):
+ for n in reversed(xrange(len(verfiles))):
if verfiles[n][:14] != 'slack-version-':
del verfiles[n]
if verfiles:
diff --git a/Lib/random.py b/Lib/random.py
index 16b667a..1f279bb 100644
--- a/Lib/random.py
+++ b/Lib/random.py
@@ -253,7 +253,7 @@ class Random(_random.Random):
if random is None:
random = self.random
- for i in xrange(len(x)-1, 0, -1):
+ for i in reversed(xrange(1, len(x))):
# pick an element in x[:i+1] with which to exchange x[i]
j = int(random() * (i+1))
x[i], x[j] = x[j], x[i]
diff --git a/Lib/rfc822.py b/Lib/rfc822.py
index 4f69b22..9a52a90 100644
--- a/Lib/rfc822.py
+++ b/Lib/rfc822.py
@@ -421,8 +421,7 @@ class Message:
hit = 0
if hit:
list.append(i)
- list.reverse()
- for i in list:
+ for i in reversed(list):
del self.headers[i]
def setdefault(self, name, default=""):
diff --git a/Lib/test/test_enumerate.py b/Lib/test/test_enumerate.py
index 88e24e8..b073606 100644
--- a/Lib/test/test_enumerate.py
+++ b/Lib/test/test_enumerate.py
@@ -124,9 +124,27 @@ class TestBig(EnumerateTestCase):
seq = range(10,20000,2)
res = zip(range(20000), seq)
+class TestReversed(unittest.TestCase):
+
+ def test_simple(self):
+ class A:
+ def __getitem__(self, i):
+ if i < 5:
+ return str(i)
+ raise StopIteration
+ def __len__(self):
+ return 5
+ for data in 'abc', range(5), tuple(enumerate('abc')), A(), xrange(1,17,5):
+ self.assertEqual(list(data)[::-1], list(reversed(data)))
+ self.assertRaises(TypeError, reversed, {})
+
+ def test_xrange_optimization(self):
+ x = xrange(1)
+ self.assertEqual(type(reversed(x)), type(iter(x)))
def test_main(verbose=None):
- testclasses = (EnumerateTestCase, SubclassTestCase, TestEmpty, TestBig)
+ testclasses = (EnumerateTestCase, SubclassTestCase, TestEmpty, TestBig,
+ TestReversed)
test_support.run_unittest(*testclasses)
# verify reference counting
diff --git a/Misc/NEWS b/Misc/NEWS
index c47ed15..838cfc8 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,9 @@ What's New in Python 2.4 alpha 1?
Core and builtins
-----------------
+- Added a reversed() builtin function that returns a reverse iterator
+ over a sequence.
+
- CObjects are now mutable (on the C level) through PyCObject_SetVoidPtr.
- list.sort() now supports three keyword arguments: cmp, key, and reverse.
diff --git a/Objects/enumobject.c b/Objects/enumobject.c
index 17a6282..998e381 100644
--- a/Objects/enumobject.c
+++ b/Objects/enumobject.c
@@ -155,3 +155,128 @@ PyTypeObject PyEnum_Type = {
enum_new, /* tp_new */
PyObject_GC_Del, /* tp_free */
};
+
+/* Reversed Object ***************************************************************/
+
+typedef struct {
+ PyObject_HEAD
+ long index;
+ PyObject* seq;
+} reversedobject;
+
+static PyObject *
+reversed_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ long n;
+ PyObject *seq;
+ reversedobject *ro;
+
+ if (!PyArg_UnpackTuple(args, "reversed", 1, 1, &seq))
+ return NULL;
+
+ /* Special case optimization for xrange */
+ if (PyRange_Check(seq))
+ return PyObject_CallMethod(seq, "__reversed__", NULL);
+
+ if (!PySequence_Check(seq)) {
+ PyErr_SetString(PyExc_TypeError,
+ "argument to reversed() must be a sequence");
+ return NULL;
+ }
+
+ n = PySequence_Size(seq);
+ if (n == -1)
+ return NULL;
+
+ ro = (reversedobject *)type->tp_alloc(type, 0);
+ if (ro == NULL)
+ return NULL;
+
+ ro->index = n-1;
+ Py_INCREF(seq);
+ ro->seq = seq;
+ return (PyObject *)ro;
+}
+
+static void
+reversed_dealloc(reversedobject *ro)
+{
+ PyObject_GC_UnTrack(ro);
+ Py_XDECREF(ro->seq);
+ ro->ob_type->tp_free(ro);
+}
+
+static int
+reversed_traverse(reversedobject *ro, visitproc visit, void *arg)
+{
+ if (ro->seq)
+ return visit((PyObject *)(ro->seq), arg);
+ return 0;
+}
+
+static PyObject *
+reversed_next(reversedobject *ro)
+{
+ PyObject *item;
+
+ if (ro->index < 0)
+ return NULL;
+
+ assert(PySequence_Check(ro->seq));
+ item = PySequence_GetItem(ro->seq, ro->index);
+ if (item == NULL)
+ return NULL;
+
+ ro->index--;
+ return item;
+}
+
+PyDoc_STRVAR(reversed_doc,
+"reverse(sequence) -> reverse iterator over values of the sequence\n"
+"\n"
+"Return a reverse iterator");
+
+PyTypeObject PyReversed_Type = {
+ PyObject_HEAD_INIT(&PyType_Type)
+ 0, /* ob_size */
+ "reversed", /* tp_name */
+ sizeof(reversedobject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)reversed_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+ Py_TPFLAGS_BASETYPE, /* tp_flags */
+ reversed_doc, /* tp_doc */
+ (traverseproc)reversed_traverse,/* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ PyObject_SelfIter, /* tp_iter */
+ (iternextfunc)reversed_next, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ PyType_GenericAlloc, /* tp_alloc */
+ reversed_new, /* tp_new */
+ PyObject_GC_Del, /* tp_free */
+};
diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c
index 299f4a6..1f56728 100644
--- a/Objects/rangeobject.c
+++ b/Objects/rangeobject.c
@@ -171,6 +171,15 @@ static PySequenceMethods range_as_sequence = {
};
static PyObject * range_iter(PyObject *seq);
+static PyObject * range_reverse(PyObject *seq);
+
+PyDoc_STRVAR(reverse_doc,
+"Returns a reverse iterator.");
+
+static PyMethodDef range_methods[] = {
+ {"__reversed__", (PyCFunction)range_reverse, METH_NOARGS, reverse_doc},
+ {NULL, NULL} /* sentinel */
+};
PyTypeObject PyRange_Type = {
PyObject_HEAD_INIT(&PyType_Type)
@@ -201,7 +210,7 @@ PyTypeObject PyRange_Type = {
0, /* tp_weaklistoffset */
(getiterfunc)range_iter, /* tp_iter */
0, /* tp_iternext */
- 0, /* tp_methods */
+ range_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
@@ -246,6 +255,32 @@ range_iter(PyObject *seq)
}
static PyObject *
+range_reverse(PyObject *seq)
+{
+ rangeiterobject *it;
+ long start, step, len;
+
+ if (!PyRange_Check(seq)) {
+ PyErr_BadInternalCall();
+ return NULL;
+ }
+ it = PyObject_New(rangeiterobject, &Pyrangeiter_Type);
+ if (it == NULL)
+ return NULL;
+
+ start = ((rangeobject *)seq)->start;
+ step = ((rangeobject *)seq)->step;
+ len = ((rangeobject *)seq)->len;
+
+ it->index = 0;
+ it->start = start + (len-1) * step;
+ it->step = -step;
+ it->len = len;
+
+ return (PyObject *)it;
+}
+
+static PyObject *
rangeiter_next(rangeiterobject *r)
{
if (r->index < r->len)
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index 0309f1de..35283fc 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -2121,6 +2121,7 @@ _PyBuiltin_Init(void)
SETBUILTIN("list", &PyList_Type);
SETBUILTIN("long", &PyLong_Type);
SETBUILTIN("object", &PyBaseObject_Type);
+ SETBUILTIN("reversed", &PyReversed_Type);
SETBUILTIN("slice", &PySlice_Type);
SETBUILTIN("staticmethod", &PyStaticMethod_Type);
SETBUILTIN("str", &PyString_Type);