Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 15d81ef

Browse files
committed
Generalize reduce() to work with iterators.
NEEDS DOC CHANGES.
1 parent 8bc10b0 commit 15d81ef

3 files changed

Lines changed: 33 additions & 12 deletions

File tree

Lib/test/test_iter.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,4 +385,17 @@ def test_builtin_map(self):
385385
except OSError:
386386
pass
387387

388+
# Test reduces()'s use of iterators.
389+
def test_builtin_reduce(self):
390+
from operator import add
391+
self.assertEqual(reduce(add, SequenceClass(5)), 10)
392+
self.assertEqual(reduce(add, SequenceClass(5), 42), 52)
393+
self.assertRaises(TypeError, reduce, add, SequenceClass(0))
394+
self.assertEqual(reduce(add, SequenceClass(0), 42), 42)
395+
self.assertEqual(reduce(add, SequenceClass(1)), 0)
396+
self.assertEqual(reduce(add, SequenceClass(1), 42), 42)
397+
398+
d = {"one": 1, "two": 2, "three": 3}
399+
self.assertEqual(reduce(add, d), "".join(d.keys()))
400+
388401
run_unittest(TestCase)

Misc/NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Core
2222
map()
2323
max()
2424
min()
25+
reduce()
2526

2627

2728
What's New in Python 2.1 (final)?

Python/bltinmodule.c

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1851,26 +1851,25 @@ is printed without a trailing newline before reading.";
18511851
static PyObject *
18521852
builtin_reduce(PyObject *self, PyObject *args)
18531853
{
1854-
PyObject *seq, *func, *result = NULL;
1855-
PySequenceMethods *sqf;
1856-
register int i;
1854+
PyObject *seq, *func, *result = NULL, *it;
18571855

18581856
if (!PyArg_ParseTuple(args, "OO|O:reduce", &func, &seq, &result))
18591857
return NULL;
18601858
if (result != NULL)
18611859
Py_INCREF(result);
18621860

1863-
sqf = seq->ob_type->tp_as_sequence;
1864-
if (sqf == NULL || sqf->sq_item == NULL) {
1861+
it = PyObject_GetIter(seq);
1862+
if (it == NULL) {
18651863
PyErr_SetString(PyExc_TypeError,
1866-
"reduce() arg 2 must be a sequence");
1864+
"reduce() arg 2 must support iteration");
1865+
Py_XDECREF(result);
18671866
return NULL;
18681867
}
18691868

18701869
if ((args = PyTuple_New(2)) == NULL)
18711870
goto Fail;
18721871

1873-
for (i = 0; ; ++i) {
1872+
for (;;) {
18741873
PyObject *op2;
18751874

18761875
if (args->ob_refcnt > 1) {
@@ -1879,12 +1878,18 @@ builtin_reduce(PyObject *self, PyObject *args)
18791878
goto Fail;
18801879
}
18811880

1882-
if ((op2 = (*sqf->sq_item)(seq, i)) == NULL) {
1883-
if (PyErr_ExceptionMatches(PyExc_IndexError)) {
1884-
PyErr_Clear();
1885-
break;
1881+
op2 = PyIter_Next(it);
1882+
if (op2 == NULL) {
1883+
/* StopIteration is *implied* by a NULL return from
1884+
* PyIter_Next() if PyErr_Occurred() is false.
1885+
*/
1886+
if (PyErr_Occurred()) {
1887+
if (PyErr_ExceptionMatches(PyExc_StopIteration))
1888+
PyErr_Clear();
1889+
else
1890+
goto Fail;
18861891
}
1887-
goto Fail;
1892+
break;
18881893
}
18891894

18901895
if (result == NULL)
@@ -1903,11 +1908,13 @@ builtin_reduce(PyObject *self, PyObject *args)
19031908
PyErr_SetString(PyExc_TypeError,
19041909
"reduce() of empty sequence with no initial value");
19051910

1911+
Py_DECREF(it);
19061912
return result;
19071913

19081914
Fail:
19091915
Py_XDECREF(args);
19101916
Py_XDECREF(result);
1917+
Py_DECREF(it);
19111918
return NULL;
19121919
}
19131920

0 commit comments

Comments
 (0)