Index: Objects/rangeobject.c =================================================================== --- Objects/rangeobject.c (revision 62616) +++ Objects/rangeobject.c (working copy) @@ -14,6 +14,7 @@ PyObject *start; PyObject *stop; PyObject *step; + PyObject *length; } rangeobject; /* Helper function for validating step. Always returns a new reference or @@ -43,6 +44,9 @@ return step; } +static PyObject* range_compute_length(PyObject *start, + PyObject *stop, PyObject *step); + /* XXX(nnorwitz): should we error check if the user passes any empty ranges? range(-10) range(0, -5) @@ -52,7 +56,7 @@ range_new(PyTypeObject *type, PyObject *args, PyObject *kw) { rangeobject *obj = NULL; - PyObject *start = NULL, *stop = NULL, *step = NULL; + PyObject *start = NULL, *stop = NULL, *step = NULL, *length = NULL; if (!_PyArg_NoKeywords("range()", kw)) return NULL; @@ -81,25 +85,30 @@ goto Fail; } + length = range_compute_length(start, stop, step); + if (length == NULL) + goto Fail; obj = PyObject_New(rangeobject, &PyRange_Type); if (obj == NULL) goto Fail; obj->start = start; obj->stop = stop; obj->step = step; + obj->length = length; return (PyObject *) obj; Fail: Py_XDECREF(start); Py_XDECREF(stop); Py_XDECREF(step); + Py_XDECREF(length); return NULL; } PyDoc_STRVAR(range_doc, "range([start,] stop[, step]) -> range object\n\ \n\ -Returns an iterator that generates the numbers in the range on demand."); +Returns a virtual sequence of numbers from start to stop by step."); static void range_dealloc(rangeobject *r) @@ -107,6 +116,7 @@ Py_DECREF(r->start); Py_DECREF(r->stop); Py_DECREF(r->step); + Py_DECREF(r->length); PyObject_Del(r); } @@ -116,8 +126,8 @@ * Arguments MUST return 1 with either PyLong_Check() or * PyLong_Check(). Return -1 when there is an error. */ -static PyObject* -range_length_obj(rangeobject *r) +static PyObject * +range_compute_length(PyObject *start, PyObject *stop, PyObject *step) { /* ------------------------------------------------------------- Algorithm is equal to that of get_len_of_range(), but it operates @@ -125,7 +135,6 @@ ---------------------------------------------------------------*/ int cmp_result, cmp_call; PyObject *lo, *hi; - PyObject *step = NULL; PyObject *diff = NULL; PyObject *one = NULL; PyObject *tmp1 = NULL, *tmp2 = NULL, *result; @@ -134,23 +143,22 @@ PyObject *zero = PyLong_FromLong(0); if (zero == NULL) return NULL; - cmp_call = PyObject_Cmp(r->step, zero, &cmp_result); + cmp_call = PyObject_Cmp(step, zero, &cmp_result); Py_DECREF(zero); if (cmp_call == -1) return NULL; assert(cmp_result != 0); if (cmp_result > 0) { - lo = r->start; - hi = r->stop; - step = r->step; + lo = start; + hi = stop; Py_INCREF(step); } else { - lo = r->stop; - hi = r->start; - step = PyNumber_Negative(r->step); + lo = stop; + hi = start; + step = PyNumber_Negative(step); if (!step) - return NULL; + goto Fail; } /* if (lo >= hi), return length of 0. */ @@ -193,46 +201,11 @@ static Py_ssize_t range_length(rangeobject *r) { - PyObject *len = range_length_obj(r); - Py_ssize_t result = -1; - if (len) { - result = PyLong_AsSsize_t(len); - Py_DECREF(len); - } - return result; + return PyLong_AsSsize_t(r->length); } -/* range(...)[x] is necessary for: seq[:] = range(...) */ static PyObject * -range_item(rangeobject *r, Py_ssize_t i) -{ - Py_ssize_t len = range_length(r); - PyObject *rem, *incr, *result; - - /* XXX(nnorwitz): should negative indices be supported? */ - /* XXX(nnorwitz): should support range[x] where x > PY_SSIZE_T_MAX? */ - if (i < 0 || i >= len) { - if (!PyErr_Occurred()) - PyErr_SetString(PyExc_IndexError, - "range object index out of range"); - return NULL; - } - - /* XXX(nnorwitz): optimize for short ints. */ - rem = PyLong_FromSsize_t(i); - if (!rem) - return NULL; - incr = PyNumber_Multiply(rem, r->step); - Py_DECREF(rem); - if (!incr) - return NULL; - result = PyNumber_Add(r->start, incr); - Py_DECREF(incr); - return result; -} - -static PyObject * range_repr(rangeobject *r) { Py_ssize_t istep; @@ -252,12 +225,40 @@ r->start, r->stop, r->step); } +static PyObject * +range_getstart(rangeobject *obj) +{ + return obj->start; +} + +static PyObject * +range_getstop(rangeobject *obj) +{ + return obj->stop; +} + +static PyObject * +range_getstep(rangeobject *obj) +{ + return obj->step; +} + +static PyGetSetDef range_getset[] = { + {"start", (getter)range_getstart, NULL, "The start of the range"}, + {"stop", (getter)range_getstop, NULL, "Where the range stops."}, + {"step", (getter)range_getstep, NULL, "How much the range jumps."}, + {0}, +}; + static PySequenceMethods range_as_sequence = { (lenfunc)range_length, /* sq_length */ - 0, /* sq_concat */ - 0, /* sq_repeat */ - (ssizeargfunc)range_item, /* sq_item */ - 0, /* sq_slice */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + 0, /* sq_contains */ }; static PyObject * range_iter(PyObject *seq); @@ -285,7 +286,7 @@ (reprfunc)range_repr, /* tp_repr */ 0, /* tp_as_number */ &range_as_sequence, /* tp_as_sequence */ - 0, /* tp_as_mapping */ + 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ @@ -302,7 +303,7 @@ 0, /* tp_iternext */ range_methods, /* tp_methods */ 0, /* tp_members */ - 0, /* tp_getset */ + range_getset, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ @@ -647,9 +648,7 @@ return NULL; /* start + (len - 1) * step */ - len = range_length_obj(range); - if (!len) - goto create_failure; + len = range->length; one = PyLong_FromLong(1); if (!one) Index: Lib/test/test_range.py =================================================================== --- Lib/test/test_range.py (revision 62616) +++ Lib/test/test_range.py (working copy) @@ -61,6 +61,16 @@ self.assertEqual(repr(range(1, 2)), 'range(1, 2)') self.assertEqual(repr(range(1, 2, 3)), 'range(1, 2, 3)') + def test_attributes(self): + r = range(0, 10, 2) + self.assertEqual(r.start, 0) + self.assertEqual(r.stop, 10) + self.assertEqual(r.step, 2) + r = range(10) + self.assertEqual(r.start, 0) + self.assertEqual(r.stop, 10) + self.assertEqual(r.step, 1) + def test_main(): test.test_support.run_unittest(RangeTest)