Skip to content

Commit 0919a1a

Browse files
committed
Part of SF patch #1513870 (the still relevant part) -- add reduce() to
functools, and adjust docs etc.
1 parent 6a2a2a0 commit 0919a1a

File tree

11 files changed

+168
-78
lines changed

11 files changed

+168
-78
lines changed

Doc/howto/doanddont.tex

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -289,19 +289,7 @@ \section{Using the Batteries}
289289
aware of for some reason: \function{min()} and \function{max()} can
290290
find the minimum/maximum of any sequence with comparable semantics,
291291
for example, yet many people write their own
292-
\function{max()}/\function{min()}. Another highly useful function is
293-
\function{reduce()}. A classical use of \function{reduce()}
294-
is something like
295-
296-
\begin{verbatim}
297-
import sys, operator
298-
nums = map(float, sys.argv[1:])
299-
print reduce(operator.add, nums)/len(nums)
300-
\end{verbatim}
301-
302-
This cute little script prints the average of all numbers given on the
303-
command line. The \function{reduce()} adds up all the numbers, and
304-
the rest is just some pre- and postprocessing.
292+
\function{max()}/\function{min()}.
305293

306294
On the same note, note that \function{float()}, \function{int()} and
307295
\function{long()} all accept arguments of type string, and so are

Doc/lib/libfuncs.tex

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -836,19 +836,6 @@ \section{Built-in Functions \label{built-in-funcs}}
836836
\end{verbatim}
837837
\end{funcdesc}
838838

839-
\begin{funcdesc}{reduce}{function, sequence\optional{, initializer}}
840-
Apply \var{function} of two arguments cumulatively to the items of
841-
\var{sequence}, from left to right, so as to reduce the sequence to
842-
a single value. For example, \code{reduce(lambda x, y: x+y, [1, 2,
843-
3, 4, 5])} calculates \code{((((1+2)+3)+4)+5)}. The left argument,
844-
\var{x}, is the accumulated value and the right argument, \var{y},
845-
is the update value from the \var{sequence}. If the optional
846-
\var{initializer} is present, it is placed before the items of the
847-
sequence in the calculation, and serves as a default when the
848-
sequence is empty. If \var{initializer} is not given and
849-
\var{sequence} contains only one item, the first item is returned.
850-
\end{funcdesc}
851-
852839
\begin{funcdesc}{reload}{module}
853840
Reload a previously imported \var{module}. The
854841
argument must be a module object, so it must have been successfully
@@ -1058,8 +1045,6 @@ \section{Built-in Functions \label{built-in-funcs}}
10581045
The \var{sequence}'s items are normally numbers, and are not allowed
10591046
to be strings. The fast, correct way to concatenate sequence of
10601047
strings is by calling \code{''.join(\var{sequence})}.
1061-
Note that \code{sum(range(\var{n}), \var{m})} is equivalent to
1062-
\code{reduce(operator.add, range(\var{n}), \var{m})}
10631048
\versionadded{2.3}
10641049
\end{funcdesc}
10651050

Doc/lib/libfunctools.tex

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,19 @@ \section{\module{functools} ---
5151
\end{verbatim}
5252
\end{funcdesc}
5353

54+
\begin{funcdesc}{reduce}{function, sequence\optional{, initializer}}
55+
Apply \var{function} of two arguments cumulatively to the items of
56+
\var{sequence}, from left to right, so as to reduce the sequence to
57+
a single value. For example, \code{reduce(lambda x, y: x+y, [1, 2,
58+
3, 4, 5])} calculates \code{((((1+2)+3)+4)+5)}. The left argument,
59+
\var{x}, is the accumulated value and the right argument, \var{y},
60+
is the update value from the \var{sequence}. If the optional
61+
\var{initializer} is present, it is placed before the items of the
62+
sequence in the calculation, and serves as a default when the
63+
sequence is empty. If \var{initializer} is not given and
64+
\var{sequence} contains only one item, the first item is returned.
65+
\end{funcdesc}
66+
5467
\begin{funcdesc}{update_wrapper}
5568
{wrapper, wrapped\optional{, assigned}\optional{, updated}}
5669
Update a wrapper function to look like the wrapped function. The optional

Doc/tut/tut.tex

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1893,8 +1893,8 @@ \subsection{Using Lists as Queues \label{lists-as-queues}}
18931893

18941894
\subsection{Functional Programming Tools \label{functional}}
18951895

1896-
There are three built-in functions that are very useful when used with
1897-
lists: \function{filter()}, \function{map()}, and \function{reduce()}.
1896+
There are two built-in functions that are very useful when used with
1897+
lists: \function{filter()} and \function{map()}.
18981898

18991899
\samp{filter(\var{function}, \var{sequence})} returns a sequence
19001900
consisting of those items from the
@@ -1934,42 +1934,6 @@ \subsection{Functional Programming Tools \label{functional}}
19341934
>>> map(add, seq, seq)
19351935
[0, 2, 4, 6, 8, 10, 12, 14]
19361936
\end{verbatim}
1937-
1938-
\samp{reduce(\var{function}, \var{sequence})} returns a single value
1939-
constructed by calling the binary function \var{function} on the first two
1940-
items of the sequence, then on the result and the next item, and so
1941-
on. For example, to compute the sum of the numbers 1 through 10:
1942-
1943-
\begin{verbatim}
1944-
>>> def add(x,y): return x+y
1945-
...
1946-
>>> reduce(add, range(1, 11))
1947-
55
1948-
\end{verbatim}
1949-
1950-
If there's only one item in the sequence, its value is returned; if
1951-
the sequence is empty, an exception is raised.
1952-
1953-
A third argument can be passed to indicate the starting value. In this
1954-
case the starting value is returned for an empty sequence, and the
1955-
function is first applied to the starting value and the first sequence
1956-
item, then to the result and the next item, and so on. For example,
1957-
1958-
\begin{verbatim}
1959-
>>> def sum(seq):
1960-
... def add(x,y): return x+y
1961-
... return reduce(add, seq, 0)
1962-
...
1963-
>>> sum(range(1, 11))
1964-
55
1965-
>>> sum([])
1966-
0
1967-
\end{verbatim}
1968-
1969-
Don't use this example's definition of \function{sum()}: since summing
1970-
numbers is such a common need, a built-in function
1971-
\code{sum(\var{sequence})} is already provided, and works exactly like
1972-
this.
19731937
\versionadded{2.3}
19741938

19751939
\subsection{List Comprehensions}
@@ -2739,7 +2703,7 @@ \section{The \function{dir()} Function \label{dir}}
27392703
'id', 'int', 'intern', 'isinstance', 'issubclass', 'iter',
27402704
'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'min',
27412705
'object', 'oct', 'open', 'ord', 'pow', 'property', 'quit', 'range',
2742-
'reduce', 'reload', 'repr', 'reversed', 'round', 'set',
2706+
'reload', 'repr', 'reversed', 'round', 'set',
27432707
'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super',
27442708
'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']
27452709
\end{verbatim}

Lib/functools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
# Copyright (C) 2006 Python Software Foundation.
88
# See C source code for _functools credits/copyright
99

10-
from _functools import partial
10+
from _functools import partial, reduce
1111

1212
# update_wrapper() and wraps() are tools to help write
1313
# wrapper functions that can handle naive introspection

Lib/test/test_functools.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,74 @@ def wrapper():
259259
self.assertEqual(wrapper.attr, 'This is a different test')
260260
self.assertEqual(wrapper.dict_attr, f.dict_attr)
261261

262+
class TestReduce(unittest.TestCase):
263+
func = functools.reduce
264+
265+
def test_reduce(self):
266+
class Squares:
267+
def __init__(self, max):
268+
self.max = max
269+
self.sofar = []
270+
271+
def __len__(self):
272+
return len(self.sofar)
273+
274+
def __getitem__(self, i):
275+
if not 0 <= i < self.max: raise IndexError
276+
n = len(self.sofar)
277+
while n <= i:
278+
self.sofar.append(n*n)
279+
n += 1
280+
return self.sofar[i]
281+
282+
self.assertEqual(self.func(lambda x, y: x+y, ['a', 'b', 'c'], ''), 'abc')
283+
self.assertEqual(
284+
self.func(lambda x, y: x+y, [['a', 'c'], [], ['d', 'w']], []),
285+
['a','c','d','w']
286+
)
287+
self.assertEqual(self.func(lambda x, y: x*y, range(2,8), 1), 5040)
288+
self.assertEqual(
289+
self.func(lambda x, y: x*y, range(2,21), 1L),
290+
2432902008176640000L
291+
)
292+
self.assertEqual(self.func(lambda x, y: x+y, Squares(10)), 285)
293+
self.assertEqual(self.func(lambda x, y: x+y, Squares(10), 0), 285)
294+
self.assertEqual(self.func(lambda x, y: x+y, Squares(0), 0), 0)
295+
self.assertRaises(TypeError, self.func)
296+
self.assertRaises(TypeError, self.func, 42, 42)
297+
self.assertRaises(TypeError, self.func, 42, 42, 42)
298+
self.assertEqual(self.func(42, "1"), "1") # func is never called with one item
299+
self.assertEqual(self.func(42, "", "1"), "1") # func is never called with one item
300+
self.assertRaises(TypeError, self.func, 42, (42, 42))
301+
302+
class BadSeq:
303+
def __getitem__(self, index):
304+
raise ValueError
305+
self.assertRaises(ValueError, self.func, 42, BadSeq())
306+
307+
# Test reduce()'s use of iterators.
308+
def test_iterator_usage(self):
309+
class SequenceClass:
310+
def __init__(self, n):
311+
self.n = n
312+
def __getitem__(self, i):
313+
if 0 <= i < self.n:
314+
return i
315+
else:
316+
raise IndexError
317+
318+
from operator import add
319+
self.assertEqual(self.func(add, SequenceClass(5)), 10)
320+
self.assertEqual(self.func(add, SequenceClass(5), 42), 52)
321+
self.assertRaises(TypeError, self.func, add, SequenceClass(0))
322+
self.assertEqual(self.func(add, SequenceClass(0), 42), 42)
323+
self.assertEqual(self.func(add, SequenceClass(1)), 0)
324+
self.assertEqual(self.func(add, SequenceClass(1), 42), 42)
325+
326+
d = {"one": 1, "two": 2, "three": 3}
327+
self.assertEqual(self.func(add, d), "".join(d.keys()))
328+
329+
262330

263331

264332
def test_main(verbose=None):
@@ -268,7 +336,8 @@ def test_main(verbose=None):
268336
TestPartialSubclass,
269337
TestPythonPartial,
270338
TestUpdateWrapper,
271-
TestWraps
339+
TestWraps,
340+
TestReduce
272341
)
273342
test_support.run_unittest(*test_classes)
274343

Misc/Vim/python.vim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ endif
6363

6464
if exists("python_highlight_builtins")
6565
syn keyword pythonBuiltin unichr all set abs vars int __import__ unicode
66-
syn keyword pythonBuiltin enumerate reduce exit issubclass
66+
syn keyword pythonBuiltin enumerate exit issubclass
6767
syn keyword pythonBuiltin divmod file Ellipsis isinstance open any
6868
syn keyword pythonBuiltin locals help filter basestring slice copyright min
6969
syn keyword pythonBuiltin super sum tuple hex execfile long id chr

Misc/cheatsheet

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -901,7 +901,7 @@ lambda [param_list]: returnedExpr
901901
-- Creates an anonymous function. returnedExpr must be
902902
an expression, not a statement (e.g., not "if xx:...",
903903
"print xxx", etc.) and thus can't contain newlines.
904-
Used mostly for filter(), map(), reduce() functions, and GUI callbacks..
904+
Used mostly for filter(), map() functions, and GUI callbacks..
905905
List comprehensions
906906
result = [expression for item1 in sequence1 [if condition1]
907907
[for item2 in sequence2 ... for itemN in sequenceN]
@@ -1005,11 +1005,6 @@ property() Created a property with access controlled by functions.
10051005
range(start [,end Returns list of ints from >= start and < end.With 1 arg,
10061006
[, step]]) list from 0..arg-1With 2 args, list from start..end-1With 3
10071007
args, list from start up to end by step
1008-
reduce(f, list [, Applies the binary function f to the items oflist so as to
1009-
init]) reduce the list to a single value.If init given, it is
1010-
"prepended" to list.
1011-
Re-parses and re-initializes an already imported module.
1012-
Useful in interactive mode, if you want to reload amodule
10131008
reload(module) after fixing it. If module was syntacticallycorrect but had
10141009
an error in initialization, mustimport it one more time
10151010
before calling reload().

Misc/python-mode.el

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ support for features needed by `python-mode'.")
386386
"isinstance" "issubclass" "iter" "len" "license"
387387
"list" "locals" "long" "map" "max" "min" "object"
388388
"oct" "open" "ord" "pow" "property" "range"
389-
"reduce" "reload" "repr" "round"
389+
"reload" "repr" "round"
390390
"setattr" "slice" "staticmethod" "str" "sum"
391391
"super" "tuple" "type" "unichr" "unicode" "vars"
392392
"zip")

Modules/_functoolsmodule.c

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,12 +242,88 @@ static PyTypeObject partial_type = {
242242
};
243243

244244

245+
/* reduce (used to be a builtin) ********************************************/
246+
247+
static PyObject *
248+
functools_reduce(PyObject *self, PyObject *args)
249+
{
250+
PyObject *seq, *func, *result = NULL, *it;
251+
252+
if (!PyArg_UnpackTuple(args, "reduce", 2, 3, &func, &seq, &result))
253+
return NULL;
254+
if (result != NULL)
255+
Py_INCREF(result);
256+
257+
it = PyObject_GetIter(seq);
258+
if (it == NULL) {
259+
PyErr_SetString(PyExc_TypeError,
260+
"reduce() arg 2 must support iteration");
261+
Py_XDECREF(result);
262+
return NULL;
263+
}
264+
265+
if ((args = PyTuple_New(2)) == NULL)
266+
goto Fail;
267+
268+
for (;;) {
269+
PyObject *op2;
270+
271+
if (args->ob_refcnt > 1) {
272+
Py_DECREF(args);
273+
if ((args = PyTuple_New(2)) == NULL)
274+
goto Fail;
275+
}
276+
277+
op2 = PyIter_Next(it);
278+
if (op2 == NULL) {
279+
if (PyErr_Occurred())
280+
goto Fail;
281+
break;
282+
}
283+
284+
if (result == NULL)
285+
result = op2;
286+
else {
287+
PyTuple_SetItem(args, 0, result);
288+
PyTuple_SetItem(args, 1, op2);
289+
if ((result = PyEval_CallObject(func, args)) == NULL)
290+
goto Fail;
291+
}
292+
}
293+
294+
Py_DECREF(args);
295+
296+
if (result == NULL)
297+
PyErr_SetString(PyExc_TypeError,
298+
"reduce() of empty sequence with no initial value");
299+
300+
Py_DECREF(it);
301+
return result;
302+
303+
Fail:
304+
Py_XDECREF(args);
305+
Py_XDECREF(result);
306+
Py_DECREF(it);
307+
return NULL;
308+
}
309+
310+
PyDoc_STRVAR(functools_reduce_doc,
311+
"reduce(function, sequence[, initial]) -> value\n\
312+
\n\
313+
Apply a function of two arguments cumulatively to the items of a sequence,\n\
314+
from left to right, so as to reduce the sequence to a single value.\n\
315+
For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates\n\
316+
((((1+2)+3)+4)+5). If initial is present, it is placed before the items\n\
317+
of the sequence in the calculation, and serves as a default when the\n\
318+
sequence is empty.");
319+
245320
/* module level code ********************************************************/
246321

247322
PyDoc_STRVAR(module_doc,
248323
"Tools that operate on functions.");
249324

250325
static PyMethodDef module_methods[] = {
326+
{"reduce", functools_reduce, METH_VARARGS, functools_reduce_doc},
251327
{NULL, NULL} /* sentinel */
252328
};
253329

Python/Python-ast.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3012,7 +3012,7 @@ init_ast(void)
30123012
if (PyDict_SetItemString(d, "AST", (PyObject*)AST_type) < 0) return;
30133013
if (PyModule_AddIntConstant(m, "PyCF_ONLY_AST", PyCF_ONLY_AST) < 0)
30143014
return;
3015-
if (PyModule_AddStringConstant(m, "__version__", "45597") < 0)
3015+
if (PyModule_AddStringConstant(m, "__version__", "51600") < 0)
30163016
return;
30173017
if (PyDict_SetItemString(d, "mod", (PyObject*)mod_type) < 0) return;
30183018
if (PyDict_SetItemString(d, "Module", (PyObject*)Module_type) < 0)

0 commit comments

Comments
 (0)