Index: Python/bltinmodule.c =================================================================== --- Python/bltinmodule.c (revision 47141) +++ Python/bltinmodule.c (working copy) @@ -1622,80 +1622,6 @@ static PyObject * -builtin_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."); - - -static PyObject * builtin_reload(PyObject *self, PyObject *v) { return PyImport_ReloadModule(v); @@ -2096,7 +2022,6 @@ {"ord", builtin_ord, METH_O, ord_doc}, {"pow", builtin_pow, METH_VARARGS, pow_doc}, {"range", builtin_range, METH_VARARGS, range_doc}, - {"reduce", builtin_reduce, METH_VARARGS, reduce_doc}, {"reload", builtin_reload, METH_O, reload_doc}, {"repr", builtin_repr, METH_O, repr_doc}, {"round", (PyCFunction)builtin_round, METH_VARARGS | METH_KEYWORDS, round_doc}, Index: Misc/Vim/python.vim =================================================================== --- Misc/Vim/python.vim (revision 47141) +++ Misc/Vim/python.vim (working copy) @@ -63,7 +63,7 @@ if exists("python_highlight_builtins") syn keyword pythonBuiltin unichr all set abs vars int __import__ unicode - syn keyword pythonBuiltin enumerate reduce exit issubclass + syn keyword pythonBuiltin enumerate exit issubclass syn keyword pythonBuiltin divmod file Ellipsis isinstance open any syn keyword pythonBuiltin locals help filter basestring slice copyright min syn keyword pythonBuiltin super sum tuple hex execfile long id chr Index: Misc/python-mode.el =================================================================== --- Misc/python-mode.el (revision 47141) +++ Misc/python-mode.el (working copy) @@ -386,7 +386,7 @@ "isinstance" "issubclass" "iter" "len" "license" "list" "locals" "long" "map" "max" "min" "object" "oct" "open" "ord" "pow" "property" "range" - "reduce" "reload" "repr" "round" + "reload" "repr" "round" "setattr" "slice" "staticmethod" "str" "sum" "super" "tuple" "type" "unichr" "unicode" "vars" "zip") Index: Misc/cheatsheet =================================================================== --- Misc/cheatsheet (revision 47141) +++ Misc/cheatsheet (working copy) @@ -901,7 +901,7 @@ -- Creates an anonymous function. returnedExpr must be an expression, not a statement (e.g., not "if xx:...", "print xxx", etc.) and thus can't contain newlines. - Used mostly for filter(), map(), reduce() functions, and GUI callbacks.. + Used mostly for filter(), map() functions, and GUI callbacks.. List comprehensions result = [expression for item1 in sequence1 [if condition1] [for item2 in sequence2 ... for itemN in sequenceN] @@ -1005,11 +1005,6 @@ range(start [,end Returns list of ints from >= start and < end.With 1 arg, [, step]]) list from 0..arg-1With 2 args, list from start..end-1With 3 args, list from start up to end by step -reduce(f, list [, Applies the binary function f to the items oflist so as to -init]) reduce the list to a single value.If init given, it is - "prepended" to list. - Re-parses and re-initializes an already imported module. - Useful in interactive mode, if you want to reload amodule reload(module) after fixing it. If module was syntacticallycorrect but had an error in initialization, mustimport it one more time before calling reload(). Index: Tools/i18n/pygettext.py =================================================================== --- Tools/i18n/pygettext.py (revision 47141) +++ Tools/i18n/pygettext.py (working copy) @@ -14,6 +14,8 @@ # # for selftesting +from functools import reduce + try: import fintl _ = fintl.gettext Index: Tools/pybench/pybench.py =================================================================== --- Tools/pybench/pybench.py (revision 47141) +++ Tools/pybench/pybench.py (working copy) @@ -49,6 +49,7 @@ import sys,time,operator from CommandLine import * +from functools import reduce try: import cPickle Index: Doc/howto/doanddont.tex =================================================================== --- Doc/howto/doanddont.tex (revision 47141) +++ Doc/howto/doanddont.tex (working copy) @@ -288,20 +288,8 @@ There are also many useful builtin functions people seem not to be aware of for some reason: \function{min()} and \function{max()} can find the minimum/maximum of any sequence with comparable semantics, -for example, yet many people write they own max/min. Another highly -useful function is \function{reduce()}. Classical use of \function{reduce()} -is something like +for example, yet many people write they own max/min. -\begin{verbatim} -import sys, operator -nums = map(float, sys.argv[1:]) -print reduce(operator.add, nums)/len(nums) -\end{verbatim} - -This cute little script prints the average of all numbers given on the -command line. The \function{reduce()} adds up all the numbers, and -the rest is just some pre- and postprocessing. - On the same note, note that \function{float()}, \function{int()} and \function{long()} all accept arguments of type string, and so are suited to parsing --- assuming you are ready to deal with the Index: Doc/lib/libfunctools.tex =================================================================== --- Doc/lib/libfunctools.tex (revision 47141) +++ Doc/lib/libfunctools.tex (working copy) @@ -51,6 +51,19 @@ \end{verbatim} \end{funcdesc} +\begin{funcdesc}{reduce}{function, sequence\optional{, initializer}} + Apply \var{function} of two arguments cumulatively to the items of + \var{sequence}, from left to right, so as to reduce the sequence to + a single value. For example, \code{reduce(lambda x, y: x+y, [1, 2, + 3, 4, 5])} calculates \code{((((1+2)+3)+4)+5)}. The left argument, + \var{x}, is the accumulated value and the right argument, \var{y}, + is the update value from the \var{sequence}. If the optional + \var{initializer} is present, it is placed before the items of the + sequence in the calculation, and serves as a default when the + sequence is empty. If \var{initializer} is not given and + \var{sequence} contains only one item, the first item is returned. +\end{funcdesc} + \begin{funcdesc}{update_wrapper} {wrapper, wrapped\optional{, assigned}\optional{, updated}} Update a wrapper function to look like the wrapped function. The optional Index: Doc/lib/libfuncs.tex =================================================================== --- Doc/lib/libfuncs.tex (revision 47141) +++ Doc/lib/libfuncs.tex (working copy) @@ -810,19 +810,6 @@ \end{verbatim} \end{funcdesc} -\begin{funcdesc}{reduce}{function, sequence\optional{, initializer}} - Apply \var{function} of two arguments cumulatively to the items of - \var{sequence}, from left to right, so as to reduce the sequence to - a single value. For example, \code{reduce(lambda x, y: x+y, [1, 2, - 3, 4, 5])} calculates \code{((((1+2)+3)+4)+5)}. The left argument, - \var{x}, is the accumulated value and the right argument, \var{y}, - is the update value from the \var{sequence}. If the optional - \var{initializer} is present, it is placed before the items of the - sequence in the calculation, and serves as a default when the - sequence is empty. If \var{initializer} is not given and - \var{sequence} contains only one item, the first item is returned. -\end{funcdesc} - \begin{funcdesc}{reload}{module} Reload a previously imported \var{module}. The argument must be a module object, so it must have been successfully @@ -1010,8 +997,6 @@ The \var{sequence}'s items are normally numbers, and are not allowed to be strings. The fast, correct way to concatenate sequence of strings is by calling \code{''.join(\var{sequence})}. - Note that \code{sum(range(\var{n}), \var{m})} is equivalent to - \code{reduce(operator.add, range(\var{n}), \var{m})} \versionadded{2.3} \end{funcdesc} Index: Doc/tut/tut.tex =================================================================== --- Doc/tut/tut.tex (revision 47141) +++ Doc/tut/tut.tex (working copy) @@ -1893,8 +1893,8 @@ \subsection{Functional Programming Tools \label{functional}} -There are three built-in functions that are very useful when used with -lists: \function{filter()}, \function{map()}, and \function{reduce()}. +There are two built-in functions that are very useful when used with +lists: \function{filter()} and \function{map()}. \samp{filter(\var{function}, \var{sequence})} returns a sequence consisting of those items from the @@ -1934,42 +1934,6 @@ >>> map(add, seq, seq) [0, 2, 4, 6, 8, 10, 12, 14] \end{verbatim} - -\samp{reduce(\var{function}, \var{sequence})} returns a single value -constructed by calling the binary function \var{function} on the first two -items of the sequence, then on the result and the next item, and so -on. For example, to compute the sum of the numbers 1 through 10: - -\begin{verbatim} ->>> def add(x,y): return x+y -... ->>> reduce(add, range(1, 11)) -55 -\end{verbatim} - -If there's only one item in the sequence, its value is returned; if -the sequence is empty, an exception is raised. - -A third argument can be passed to indicate the starting value. In this -case the starting value is returned for an empty sequence, and the -function is first applied to the starting value and the first sequence -item, then to the result and the next item, and so on. For example, - -\begin{verbatim} ->>> def sum(seq): -... def add(x,y): return x+y -... return reduce(add, seq, 0) -... ->>> sum(range(1, 11)) -55 ->>> sum([]) -0 -\end{verbatim} - -Don't use this example's definition of \function{sum()}: since summing -numbers is such a common need, a built-in function -\code{sum(\var{sequence})} is already provided, and works exactly like -this. \versionadded{2.3} \subsection{List Comprehensions} @@ -2739,7 +2703,7 @@ 'id', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'min', 'object', 'oct', 'open', 'ord', 'pow', 'property', 'quit', 'range', - 'reduce', 'reload', 'repr', 'reversed', 'round', 'set', + 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip'] \end{verbatim} Index: Lib/idlelib/AutoCompleteWindow.py =================================================================== --- Lib/idlelib/AutoCompleteWindow.py (revision 47141) +++ Lib/idlelib/AutoCompleteWindow.py (working copy) @@ -3,6 +3,7 @@ """ from Tkinter import * from MultiCall import MC_SHIFT +from functools import reduce import AutoComplete HIDE_VIRTUAL_EVENT_NAME = "<>" Index: Lib/idlelib/MultiCall.py =================================================================== --- Lib/idlelib/MultiCall.py (revision 47141) +++ Lib/idlelib/MultiCall.py (working copy) @@ -34,6 +34,7 @@ import string import re import Tkinter +from functools import reduce # the event type constants, which define the meaning of mc_type MC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3; Index: Lib/difflib.py =================================================================== --- Lib/difflib.py (revision 47141) +++ Lib/difflib.py (working copy) @@ -33,6 +33,7 @@ 'unified_diff', 'HtmlDiff'] import heapq +from functools import reduce def _calculate_ratio(matches, length): if length: Index: Lib/csv.py =================================================================== --- Lib/csv.py (revision 47141) +++ Lib/csv.py (working copy) @@ -10,6 +10,7 @@ QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE, \ __doc__ from _csv import Dialect as _Dialect +from functools import reduce try: from cStringIO import StringIO Index: Lib/test/test_functools.py =================================================================== --- Lib/test/test_functools.py (revision 47141) +++ Lib/test/test_functools.py (working copy) @@ -259,8 +259,76 @@ self.assertEqual(wrapper.attr, 'This is a different test') self.assertEqual(wrapper.dict_attr, f.dict_attr) +class TestReduce(unittest.TestCase): + func = functools.reduce + 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] + + self.assertEqual(self.func(lambda x, y: x+y, ['a', 'b', 'c'], ''), 'abc') + self.assertEqual( + self.func(lambda x, y: x+y, [['a', 'c'], [], ['d', 'w']], []), + ['a','c','d','w'] + ) + self.assertEqual(self.func(lambda x, y: x*y, range(2,8), 1), 5040) + self.assertEqual( + self.func(lambda x, y: x*y, range(2,21), 1L), + 2432902008176640000L + ) + self.assertEqual(self.func(lambda x, y: x+y, Squares(10)), 285) + self.assertEqual(self.func(lambda x, y: x+y, Squares(10), 0), 285) + self.assertEqual(self.func(lambda x, y: x+y, Squares(0), 0), 0) + self.assertRaises(TypeError, self.func) + self.assertRaises(TypeError, self.func, 42, 42) + self.assertRaises(TypeError, self.func, 42, 42, 42) + self.assertEqual(self.func(42, "1"), "1") # func is never called with one item + self.assertEqual(self.func(42, "", "1"), "1") # func is never called with one item + self.assertRaises(TypeError, self.func, 42, (42, 42)) + + class BadSeq: + def __getitem__(self, index): + raise ValueError + self.assertRaises(ValueError, self.func, 42, BadSeq()) + + # Test reduce()'s use of iterators. + def test_iterator_usage(self): + class SequenceClass: + def __init__(self, n): + self.n = n + def __getitem__(self, i): + if 0 <= i < self.n: + return i + else: + raise IndexError + + from operator import add + self.assertEqual(self.func(add, SequenceClass(5)), 10) + self.assertEqual(self.func(add, SequenceClass(5), 42), 52) + self.assertRaises(TypeError, self.func, add, SequenceClass(0)) + self.assertEqual(self.func(add, SequenceClass(0), 42), 42) + self.assertEqual(self.func(add, SequenceClass(1)), 0) + self.assertEqual(self.func(add, SequenceClass(1), 42), 42) + + d = {"one": 1, "two": 2, "three": 3} + self.assertEqual(self.func(add, d), "".join(d.keys())) + + + + def test_main(verbose=None): import sys test_classes = ( @@ -268,7 +336,8 @@ TestPartialSubclass, TestPythonPartial, TestUpdateWrapper, - TestWraps + TestWraps, + TestReduce ) test_support.run_unittest(*test_classes) Index: Lib/test/test_iter.py =================================================================== --- Lib/test/test_iter.py (revision 47141) +++ Lib/test/test_iter.py (working copy) @@ -498,19 +498,6 @@ for y in NoGuessLen5(), Guess3Len5(), Guess30Len5(): self.assertEqual(zip(x, y), expected) - # 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())) - # This test case will be removed if we don't have Unicode def test_unicode_join_endcase(self): Index: Lib/test/test_random.py =================================================================== --- Lib/test/test_random.py (revision 47141) +++ Lib/test/test_random.py (working copy) @@ -6,6 +6,7 @@ import pickle import warnings from math import log, exp, sqrt, pi +from functools import reduce from test import test_support class TestBasicOps(unittest.TestCase): Index: Lib/test/test_builtin.py =================================================================== --- Lib/test/test_builtin.py (revision 47141) +++ Lib/test/test_builtin.py (working copy) @@ -1380,32 +1380,6 @@ self.assertRaises(OverflowError, range, -sys.maxint, sys.maxint) self.assertRaises(OverflowError, range, 0, 2*sys.maxint) - def test_reduce(self): - 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)) - - class BadSeq: - def __getitem__(self, index): - raise ValueError - self.assertRaises(ValueError, reduce, 42, BadSeq()) - def test_reload(self): import marshal reload(marshal) Index: Lib/functools.py =================================================================== --- Lib/functools.py (revision 47141) +++ Lib/functools.py (working copy) @@ -7,7 +7,7 @@ # Copyright (C) 2006 Python Software Foundation. # See C source code for _functools credits/copyright -from _functools import partial +from _functools import partial, reduce # update_wrapper() and wraps() are tools to help write # wrapper functions that can handle naive introspection Index: Modules/_functoolsmodule.c =================================================================== --- Modules/_functoolsmodule.c (revision 47141) +++ Modules/_functoolsmodule.c (working copy) @@ -242,12 +242,88 @@ }; +/* reduce (used to be a builtin) ********************************************/ + +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(functools_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."); + /* module level code ********************************************************/ PyDoc_STRVAR(module_doc, "Tools that operate on functions."); static PyMethodDef module_methods[] = { + {"reduce", functools_reduce, METH_VARARGS, functools_reduce_doc}, {NULL, NULL} /* sentinel */ };