mirror of
https://github.com/python/cpython.git
synced 2024-11-23 09:54:58 +08:00
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()}.
|
||||
\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.
|
||||
|
@ -8,6 +8,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
PyAPI_DATA(PyTypeObject) PyEnum_Type;
|
||||
PyAPI_DATA(PyTypeObject) PyReversed_Type;
|
||||
|
||||
#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
|
||||
# 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
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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]
|
||||
|
@ -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=""):
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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 */
|
||||
};
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user