From 83e818415a591a14677ae6985d3bb0a035cd4c48 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sat, 9 Aug 2008 23:30:55 +0000 Subject: Copy reduce() to _functools so to have functools.reduce() not raise a warning from usage under -3. --- Lib/functools.py | 3 +- Lib/test/test_functools.py | 45 +++++++++++++++++++++++++- Misc/NEWS | 3 ++ Modules/_functoolsmodule.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 3 deletions(-) diff --git a/Lib/functools.py b/Lib/functools.py index 30e1d24..a54f030 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -7,8 +7,7 @@ # Copyright (C) 2006 Python Software Foundation. # See C source code for _functools credits/copyright -from _functools import partial -from __builtin__ import reduce +from _functools import partial, reduce # update_wrapper() and wraps() are tools to help write # wrapper functions that can handle naive introspection diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 6012f9f..30d419d 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -267,6 +267,48 @@ class TestWraps(TestUpdateWrapper): self.assertEqual(wrapper.dict_attr, f.dict_attr) +class TestReduce(unittest.TestCase): + + def test_reduce(self): + class Squares: + + def __init__(self, max): + self.max = max + self.sofar = [] + + def __len__(self): return len(self.sofar) + + def __getitem__(self, i): + if not 0 <= i < self.max: raise IndexError + n = len(self.sofar) + while n <= i: + self.sofar.append(n*n) + n += 1 + return self.sofar[i] + + reduce = functools.reduce + self.assertEqual(reduce(lambda x, y: x+y, ['a', 'b', 'c'], ''), 'abc') + self.assertEqual( + reduce(lambda x, y: x+y, [['a', 'c'], [], ['d', 'w']], []), + ['a','c','d','w'] + ) + self.assertEqual(reduce(lambda x, y: x*y, range(2,8), 1), 5040) + self.assertEqual( + reduce(lambda x, y: x*y, range(2,21), 1L), + 2432902008176640000L + ) + self.assertEqual(reduce(lambda x, y: x+y, Squares(10)), 285) + self.assertEqual(reduce(lambda x, y: x+y, Squares(10), 0), 285) + self.assertEqual(reduce(lambda x, y: x+y, Squares(0), 0), 0) + self.assertRaises(TypeError, reduce) + self.assertRaises(TypeError, reduce, 42, 42) + self.assertRaises(TypeError, reduce, 42, 42, 42) + self.assertEqual(reduce(42, "1"), "1") # func is never called with one item + self.assertEqual(reduce(42, "", "1"), "1") # func is never called with one item + self.assertRaises(TypeError, reduce, 42, (42, 42)) + + + def test_main(verbose=None): import sys @@ -275,7 +317,8 @@ def test_main(verbose=None): TestPartialSubclass, TestPythonPartial, TestUpdateWrapper, - TestWraps + TestWraps, + TestReduce, ) test_support.run_unittest(*test_classes) diff --git a/Misc/NEWS b/Misc/NEWS index 582bd22..60a783d 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -41,6 +41,9 @@ Core and Builtins Library ------- +- Copied the implementation of reduce() to _functools.reduce() to have a + version that did not raise a DeprecationWarning under -3. + - Issue #3205: When iterating over a BZ2File fails allocating memory, raise a MemoryError rather than silently stop the iteration. diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 32b246b..5dfbc15 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -9,6 +9,84 @@ All rights reserved. */ +/* reduce() *************************************************************/ + +static PyObject * +functools_reduce(PyObject *self, PyObject *args) +{ + PyObject *seq, *func, *result = NULL, *it; + + if (!PyArg_UnpackTuple(args, "reduce", 2, 3, &func, &seq, &result)) + return NULL; + if (result != NULL) + Py_INCREF(result); + + it = PyObject_GetIter(seq); + if (it == NULL) { + PyErr_SetString(PyExc_TypeError, + "reduce() arg 2 must support iteration"); + Py_XDECREF(result); + return NULL; + } + + if ((args = PyTuple_New(2)) == NULL) + goto Fail; + + for (;;) { + PyObject *op2; + + if (args->ob_refcnt > 1) { + Py_DECREF(args); + if ((args = PyTuple_New(2)) == NULL) + goto Fail; + } + + op2 = PyIter_Next(it); + if (op2 == NULL) { + if (PyErr_Occurred()) + goto Fail; + break; + } + + if (result == NULL) + result = op2; + else { + PyTuple_SetItem(args, 0, result); + PyTuple_SetItem(args, 1, op2); + if ((result = PyEval_CallObject(func, args)) == NULL) + goto Fail; + } + } + + Py_DECREF(args); + + if (result == NULL) + 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; +} + +PyDoc_STRVAR(reduce_doc, +"reduce(function, sequence[, initial]) -> value\n\ +\n\ +Apply a function of two arguments cumulatively to the items of a sequence,\n\ +from left to right, so as to reduce the sequence to a single value.\n\ +For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates\n\ +((((1+2)+3)+4)+5). If initial is present, it is placed before the items\n\ +of the sequence in the calculation, and serves as a default when the\n\ +sequence is empty."); + + + + /* partial object **********************************************************/ typedef struct { @@ -247,6 +325,7 @@ PyDoc_STRVAR(module_doc, "Tools that operate on functions."); static PyMethodDef module_methods[] = { + {"reduce", functools_reduce, METH_VARARGS, reduce_doc}, {NULL, NULL} /* sentinel */ }; -- cgit v0.12