mirror of https://github.com/python/cpython
Implement and apply PEP 322, reverse iteration
This commit is contained in:
parent
f607fc5395
commit
85c20a41df
|
@ -880,6 +880,14 @@ class C(object):
|
||||||
when passed to \function{eval()}.
|
when passed to \function{eval()}.
|
||||||
\end{funcdesc}
|
\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}}
|
\begin{funcdesc}{round}{x\optional{, n}}
|
||||||
Return the floating point value \var{x} rounded to \var{n} digits
|
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.
|
after the decimal point. If \var{n} is omitted, it defaults to zero.
|
||||||
|
|
|
@ -8,6 +8,7 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
PyAPI_DATA(PyTypeObject) PyEnum_Type;
|
PyAPI_DATA(PyTypeObject) PyEnum_Type;
|
||||||
|
PyAPI_DATA(PyTypeObject) PyReversed_Type;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
# 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
|
# 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.
|
# (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)
|
_siftup(x, i)
|
||||||
|
|
||||||
# 'heap' is a heap at all indices >= startpos, except possibly for pos. pos
|
# 'heap' is a heap at all indices >= startpos, except possibly for pos. pos
|
||||||
|
|
|
@ -975,8 +975,7 @@ def test():
|
||||||
print seqs
|
print seqs
|
||||||
f.putsequences(seqs)
|
f.putsequences(seqs)
|
||||||
do('f.getsequences()')
|
do('f.getsequences()')
|
||||||
testfolders.reverse()
|
for t in reversed(testfolders): do('mh.deletefolder(%s)' % `t`)
|
||||||
for t in testfolders: do('mh.deletefolder(%s)' % `t`)
|
|
||||||
do('mh.getcontext()')
|
do('mh.getcontext()')
|
||||||
context = mh.getcontext()
|
context = mh.getcontext()
|
||||||
f = mh.openfolder(context)
|
f = mh.openfolder(context)
|
||||||
|
|
|
@ -201,7 +201,7 @@ def _dist_try_harder(distname,version,id):
|
||||||
if os.path.isdir('/usr/lib/setup'):
|
if os.path.isdir('/usr/lib/setup'):
|
||||||
# Check for slackware verson tag file (thanks to Greg Andruk)
|
# Check for slackware verson tag file (thanks to Greg Andruk)
|
||||||
verfiles = os.listdir('/usr/lib/setup')
|
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-':
|
if verfiles[n][:14] != 'slack-version-':
|
||||||
del verfiles[n]
|
del verfiles[n]
|
||||||
if verfiles:
|
if verfiles:
|
||||||
|
|
|
@ -253,7 +253,7 @@ class Random(_random.Random):
|
||||||
|
|
||||||
if random is None:
|
if random is None:
|
||||||
random = self.random
|
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]
|
# pick an element in x[:i+1] with which to exchange x[i]
|
||||||
j = int(random() * (i+1))
|
j = int(random() * (i+1))
|
||||||
x[i], x[j] = x[j], x[i]
|
x[i], x[j] = x[j], x[i]
|
||||||
|
|
|
@ -421,8 +421,7 @@ class Message:
|
||||||
hit = 0
|
hit = 0
|
||||||
if hit:
|
if hit:
|
||||||
list.append(i)
|
list.append(i)
|
||||||
list.reverse()
|
for i in reversed(list):
|
||||||
for i in list:
|
|
||||||
del self.headers[i]
|
del self.headers[i]
|
||||||
|
|
||||||
def setdefault(self, name, default=""):
|
def setdefault(self, name, default=""):
|
||||||
|
|
|
@ -124,9 +124,27 @@ class TestBig(EnumerateTestCase):
|
||||||
seq = range(10,20000,2)
|
seq = range(10,20000,2)
|
||||||
res = zip(range(20000), seq)
|
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):
|
def test_main(verbose=None):
|
||||||
testclasses = (EnumerateTestCase, SubclassTestCase, TestEmpty, TestBig)
|
testclasses = (EnumerateTestCase, SubclassTestCase, TestEmpty, TestBig,
|
||||||
|
TestReversed)
|
||||||
test_support.run_unittest(*testclasses)
|
test_support.run_unittest(*testclasses)
|
||||||
|
|
||||||
# verify reference counting
|
# verify reference counting
|
||||||
|
|
|
@ -12,6 +12,9 @@ What's New in Python 2.4 alpha 1?
|
||||||
Core and builtins
|
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.
|
- CObjects are now mutable (on the C level) through PyCObject_SetVoidPtr.
|
||||||
|
|
||||||
- list.sort() now supports three keyword arguments: cmp, key, and reverse.
|
- list.sort() now supports three keyword arguments: cmp, key, and reverse.
|
||||||
|
|
|
@ -155,3 +155,128 @@ PyTypeObject PyEnum_Type = {
|
||||||
enum_new, /* tp_new */
|
enum_new, /* tp_new */
|
||||||
PyObject_GC_Del, /* tp_free */
|
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 */
|
||||||
|
};
|
||||||
|
|
|
@ -171,6 +171,15 @@ static PySequenceMethods range_as_sequence = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyObject * range_iter(PyObject *seq);
|
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 = {
|
PyTypeObject PyRange_Type = {
|
||||||
PyObject_HEAD_INIT(&PyType_Type)
|
PyObject_HEAD_INIT(&PyType_Type)
|
||||||
|
@ -201,7 +210,7 @@ PyTypeObject PyRange_Type = {
|
||||||
0, /* tp_weaklistoffset */
|
0, /* tp_weaklistoffset */
|
||||||
(getiterfunc)range_iter, /* tp_iter */
|
(getiterfunc)range_iter, /* tp_iter */
|
||||||
0, /* tp_iternext */
|
0, /* tp_iternext */
|
||||||
0, /* tp_methods */
|
range_methods, /* tp_methods */
|
||||||
0, /* tp_members */
|
0, /* tp_members */
|
||||||
0, /* tp_getset */
|
0, /* tp_getset */
|
||||||
0, /* tp_base */
|
0, /* tp_base */
|
||||||
|
@ -245,6 +254,32 @@ range_iter(PyObject *seq)
|
||||||
return (PyObject *)it;
|
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 *
|
static PyObject *
|
||||||
rangeiter_next(rangeiterobject *r)
|
rangeiter_next(rangeiterobject *r)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2121,6 +2121,7 @@ _PyBuiltin_Init(void)
|
||||||
SETBUILTIN("list", &PyList_Type);
|
SETBUILTIN("list", &PyList_Type);
|
||||||
SETBUILTIN("long", &PyLong_Type);
|
SETBUILTIN("long", &PyLong_Type);
|
||||||
SETBUILTIN("object", &PyBaseObject_Type);
|
SETBUILTIN("object", &PyBaseObject_Type);
|
||||||
|
SETBUILTIN("reversed", &PyReversed_Type);
|
||||||
SETBUILTIN("slice", &PySlice_Type);
|
SETBUILTIN("slice", &PySlice_Type);
|
||||||
SETBUILTIN("staticmethod", &PyStaticMethod_Type);
|
SETBUILTIN("staticmethod", &PyStaticMethod_Type);
|
||||||
SETBUILTIN("str", &PyString_Type);
|
SETBUILTIN("str", &PyString_Type);
|
||||||
|
|
Loading…
Reference in New Issue