]> granicus.if.org Git - python/commitdiff
Implement and apply PEP 322, reverse iteration
authorRaymond Hettinger <python@rcn.com>
Thu, 6 Nov 2003 14:06:48 +0000 (14:06 +0000)
committerRaymond Hettinger <python@rcn.com>
Thu, 6 Nov 2003 14:06:48 +0000 (14:06 +0000)
12 files changed:
Doc/lib/libfuncs.tex
Include/enumobject.h
Lib/heapq.py
Lib/mhlib.py
Lib/platform.py
Lib/random.py
Lib/rfc822.py
Lib/test/test_enumerate.py
Misc/NEWS
Objects/enumobject.c
Objects/rangeobject.c
Python/bltinmodule.c

index a9f3a65a70245140e051a669b88850d4926105d0..7d64f936380e57f16928397b86616cc42af52225 100644 (file)
@@ -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.
index 053fb72a5ec50a05f8aacdca15b323a102b9805e..c14dbfc8c37e7c9316b9cca0a5969ee925d729d8 100644 (file)
@@ -8,6 +8,7 @@ extern "C" {
 #endif
 
 PyAPI_DATA(PyTypeObject) PyEnum_Type;
+PyAPI_DATA(PyTypeObject) PyReversed_Type;
 
 #ifdef __cplusplus
 }
index 2c30b12f220de0f47f168ecf7b0dca67049b5ec6..2223a97fa4c7ee50928238449b9ec534f8823fbd 100644 (file)
@@ -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
index 5520f8238e6a8202dd07aa41352746259c3fd505..899939a4937f5d64c3d97a1a21cdadbc5e6c354c 100644 (file)
@@ -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)
index 31c47b4c9ad0120c5f03716e7d50cbf3d0b9d22d..389e50cb2c7bf5fdbbe6a80088fcfc461d85ce15 100755 (executable)
@@ -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:
index 16b667a3af2b05c2285a1b4e9c9faf293f0ac18b..1f279bb8782700f8690ee2e9ad88a85f72aca636 100644 (file)
@@ -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]
index 4f69b22aabdeb6506433d7b9baa808d2fc00c2e1..9a52a90d1dbfa38537dcc32734d51cf18ecf4c86 100644 (file)
@@ -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=""):
index 88e24e8aba9e34d197a4aa32c13d93def93d1195..b07360600a0ca84162b1a1efc670b70eb6b9ebee 100644 (file)
@@ -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
index c47ed158afc66000e2b627912fe312497d694a7b..838cfc8009b5928a98032e83e598650f47557348 100644 (file)
--- 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.
index 17a62825a0991c7775521d344288ad13736508a2..998e3810a1dd17a3f196b052d126b9c89837f110 100644 (file)
@@ -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 */
+};
index 299f4a651f9fb779534343cd39eed92935a3a5c7..1f5672804f31c2acbfbdda2031d2c16cf32fdb8c 100644 (file)
@@ -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 */
@@ -245,6 +254,32 @@ range_iter(PyObject *seq)
        return (PyObject *)it;
 }
 
+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)
 {
index 0309f1de55997af0928c5969d71104f228b21f05..35283fc00ca61d330e93c7c92c1c60a0a96eda57 100644 (file)
@@ -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);