Implement and apply PEP 322, reverse iteration

This commit is contained in:
Raymond Hettinger 2003-11-06 14:06:48 +00:00
parent f607fc5395
commit 85c20a41df
12 changed files with 198 additions and 9 deletions

View 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.

View File

@ -8,6 +8,7 @@ extern "C" {
#endif
PyAPI_DATA(PyTypeObject) PyEnum_Type;
PyAPI_DATA(PyTypeObject) PyReversed_Type;
#ifdef __cplusplus
}

View 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

View 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)

View File

@ -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:

View 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]

View 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=""):

View 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

View File

@ -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.

View 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 */
};

View 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)
{

View 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);