diff options
-rw-r--r-- | Lib/test/test_iter.py | 13 | ||||
-rw-r--r-- | Misc/NEWS | 1 | ||||
-rw-r--r-- | Python/bltinmodule.c | 31 |
3 files changed, 33 insertions, 12 deletions
diff --git a/Lib/test/test_iter.py b/Lib/test/test_iter.py index 66f40c1..5584587 100644 --- a/Lib/test/test_iter.py +++ b/Lib/test/test_iter.py @@ -385,4 +385,17 @@ class TestCase(unittest.TestCase): except OSError: pass + # Test reduces()'s use of iterators. + def test_builtin_reduce(self): + from operator import add + self.assertEqual(reduce(add, SequenceClass(5)), 10) + self.assertEqual(reduce(add, SequenceClass(5), 42), 52) + self.assertRaises(TypeError, reduce, add, SequenceClass(0)) + self.assertEqual(reduce(add, SequenceClass(0), 42), 42) + self.assertEqual(reduce(add, SequenceClass(1)), 0) + self.assertEqual(reduce(add, SequenceClass(1), 42), 42) + + d = {"one": 1, "two": 2, "three": 3} + self.assertEqual(reduce(add, d), "".join(d.keys())) + run_unittest(TestCase) @@ -22,6 +22,7 @@ Core map() max() min() + reduce() What's New in Python 2.1 (final)? diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 0c20d10..5209607 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1851,26 +1851,25 @@ is printed without a trailing newline before reading."; static PyObject * builtin_reduce(PyObject *self, PyObject *args) { - PyObject *seq, *func, *result = NULL; - PySequenceMethods *sqf; - register int i; + PyObject *seq, *func, *result = NULL, *it; if (!PyArg_ParseTuple(args, "OO|O:reduce", &func, &seq, &result)) return NULL; if (result != NULL) Py_INCREF(result); - sqf = seq->ob_type->tp_as_sequence; - if (sqf == NULL || sqf->sq_item == NULL) { + it = PyObject_GetIter(seq); + if (it == NULL) { PyErr_SetString(PyExc_TypeError, - "reduce() arg 2 must be a sequence"); + "reduce() arg 2 must support iteration"); + Py_XDECREF(result); return NULL; } if ((args = PyTuple_New(2)) == NULL) goto Fail; - for (i = 0; ; ++i) { + for (;;) { PyObject *op2; if (args->ob_refcnt > 1) { @@ -1879,12 +1878,18 @@ builtin_reduce(PyObject *self, PyObject *args) goto Fail; } - if ((op2 = (*sqf->sq_item)(seq, i)) == NULL) { - if (PyErr_ExceptionMatches(PyExc_IndexError)) { - PyErr_Clear(); - break; + op2 = PyIter_Next(it); + if (op2 == NULL) { + /* StopIteration is *implied* by a NULL return from + * PyIter_Next() if PyErr_Occurred() is false. + */ + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_StopIteration)) + PyErr_Clear(); + else + goto Fail; } - goto Fail; + break; } if (result == NULL) @@ -1903,11 +1908,13 @@ builtin_reduce(PyObject *self, PyObject *args) PyErr_SetString(PyExc_TypeError, "reduce() of empty sequence with no initial value"); + Py_DECREF(it); return result; Fail: Py_XDECREF(args); Py_XDECREF(result); + Py_DECREF(it); return NULL; } |